ListView addHeaderView заставляет позицию увеличиваться на единицу?

Ниже приведен фрагмент кода с ListView. Я добавил пустой вид и заголовок. Добавление headerView приводит к увеличению позиции в onItemClick на единицу.

Так что без headerView первый элемент списка будет иметь позицию 0, с headerView позиция первого элемента списка будет 1!

Это вызывает ошибки в моем адаптере, например. при вызове getItem() и использовании некоторых других методов см. ниже. Странная вещь: в методе адаптера getView() первый элемент списка запрашивается с позицией 0, даже если добавляется headerView!!

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ListView list = (ListView) viewSwitcher.findViewById(R.id.list);
    View emptyView = viewSwitcher.findViewById(R.id.empty);
    list.setEmptyView(emptyView);

    View sectionHeading = inflater.inflate(R.layout.heading, list, false);
    TextView sectionHeadingTextView = (TextView) sectionHeading.findViewById(R.id.headingText);
    sectionHeadingTextView.setText(headerText);
    list.addHeaderView(sectionHeading);

    list.setAdapter(listAdapter);

    list.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            listAdapter.getItem(position);
            //Do something with it
        }
    });
}

Некоторые методы адаптера:

@Override
public int getViewTypeCount() {
    return TYPE_COUNT;
}

@Override
public int getItemViewType(int position) {
    return (position < items.size()) ? ITEM_NORMAL : ITEM_ADVANCED;
}

    @Override
public Product getItem(int position) {
    return items.get(position);
}
@Override
public boolean areAllItemsEnabled() {
    return false;
}

@Override
public boolean isEnabled(int position) {
    return (position < items.size());
}

Это нормальное поведение при добавлении headerView?? И как побороть проблемы в моем адаптере?


person d1rk    schedule 19.06.2012    source источник
comment
В вашем вопросе был ответ на мой вопрос. +1   -  person Shashank Degloorkar    schedule 18.03.2014


Ответы (11)


Я только что столкнулся с этой проблемой, и кажется, что лучше всего использовать ListView.getItemAtPosition(position) вместо ListAdapter.getItem(position), поскольку версия ListView учитывает заголовки, то есть: -

Сделайте это вместо этого:

myListView.getItemAtPosition(position)
person Ian Warwick    schedule 15.11.2012
comment
Так хорошо! onItemClick дает вам parent по какой-то причине. Вы можете использовать parent.getItemAtPosition(position) - person Brais Gabin; 12.06.2013
comment
На самом деле это не сработало для меня, в то время как использование адаптера @whuandroid с декором / оболочкой работает. - person Dori; 20.08.2013
comment
также вы должны разыграть предмет в этом случае. - person njzk2; 23.12.2013
comment
У меня не работает, даже если я передаю parent в ListView. Мне просто нужно исключить position == 0... - person PawelP; 03.01.2014
comment
У меня не работает. AdapterView также просто переходит в добавленный listAdapter. return (adapter == null || position < 0) ? null : adapter.getItem(position); - person philipp; 26.02.2015
comment
Оно работает. Я столкнулся со странной проблемой, когда внутри метода «onListItemClick», когда я пытался получить адаптер данных с помощью метода «getAdapter», он возвращал мне представление «Заголовок», а не адаптер, который я использовал с «setAdapter». С этим решением нет необходимости получать адаптер, и можно напрямую использовать родительское представление (список). Большое спасибо. - person sandip; 27.02.2016

Если вас не волнует клик на заголовок, вычтите количество просмотров заголовка из позиции, чтобы получить позицию для вашего адаптера:

        listView.addHeaderView(inflater.inflate(
                R.layout.my_hdr_layout, null), null, false);
        listView.setAdapter(m_adapter);
        listView.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {
            position -= listView.getHeaderViewsCount();
            final MyObject object = m_adapter.getItem(position);

        }
    });
person farid_z    schedule 17.04.2013
comment
Это должен быть лучший ответ, меня не волнует заголовок, мне важен элемент списка. - person Ninja; 28.07.2016
comment
Я думаю, что это решение не работает для последней версии ОС 7.0. - person sha; 19.10.2016

Если вы добавите заголовок к ListView, он станет первым элементом в списке. Вы можете компенсировать это, переопределив getCount(), чтобы возвращать на один элемент меньше, но убедитесь, что вы обрабатываете getItem(), getItemId() и т. д., поскольку заголовок будет элементом в позиции 0.

person akshaydashrath    schedule 19.06.2012
comment
Переопределение getCount() для возврата на один элемент меньше не работает, потому что тогда я пропускаю последний элемент списка в списке. Если в списке только 1 элемент, то getView() никогда не вызывается. Кроме того, адаптер отделен от списка и не знает, был ли добавлен заголовок в список или нет. - person d1rk; 20.06.2012
comment
В ListItemClick установите position = position - 1, если есть прикрепленный заголовок, я думаю, что это то, что я сделал, когда столкнулся с этой конкретной проблемой, это не очень аккуратно, но единственный способ, который я могу придумать - person akshaydashrath; 20.06.2012
comment
Я предполагаю, что самым простым способом было бы установить position = position-(количество добавленных просмотров заголовков) в OnItemClickListener, как вы предложили. Я изменил свой адаптер для обработки самого представления заголовка, поэтому мне не нужно вызывать addHeaderView в представлении списка. Хотя адаптер стал немного сложнее. - person d1rk; 25.07.2012
comment
Количество добавленных заголовков можно получить с помощью: ListView.getHeaderViewsCount() - person John; 19.11.2012
comment
Я не понимаю, насколько это правильный ответ. @Ian Warwick опубликовал правильный, хотя, я полагаю, немного поздно. - person sidon; 16.01.2013
comment
Несмотря на то, что это проверено как ответ, на самом деле это пользователь 1705590, который прав. НЕ используйте адаптер, который вы передаете в список. Вместо этого запросите в списке его адаптер. Если вы используете адаптер, полученный из myListView.getAdapter, позиция будет правильной, игнорируя как верхние, так и нижние колонтитулы. - person G. Blake Meike; 20.01.2013
comment
Понижено, потому что излишне сложно. Ответ Яна лучше. Также см. комментарий Джона выше - person Tariq; 10.07.2015

При реализации AdapterView.OnItemClickListener мы просто вычитаем заголовки из позиции.

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    position -= mListView.getHeaderViewsCount();
    // what ever else follows...
}
person philipp    schedule 26.02.2015

Нет необходимости менять какой-либо код. Просто используйте приведенный ниже код.

 list.setOnItemClickListener(new OnItemClickListener() {
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      parent.getAdapter().getItem(position);
         //Do something with it
     }
});
person Ramesh Akula    schedule 27.02.2013
comment
Обычно для этого требуется кастинг, так как возвращаемый элемент — это просто Object. - person android developer; 10.06.2014

Не используйте свой адаптер, используйте «украшенный» адаптер от getAdapter.

person cycDroid    schedule 20.01.2013
comment
+1 этот IMO - правильный путь - адаптер, который вы предоставляете, обернут другим адаптером, который учитывает смещение индекса при указании заголовка. Этот код для этого используется @Ramesh в другом месте на этой странице (+1 и вам!) - person Dori; 20.08.2013

Одним из решений этой проблемы является сопоставление идентификатора с индексом. В основном заставить id возвращать индекс в списке, и прослушиватель кликов будет делать:

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            listAdapter.getItem((int) id);
}
person Heinrisch    schedule 21.11.2012

Привет, мы можем решить эту проблему таким образом, сначала вы создаете представление заголовка, после того, как вводите в свой список, и на последнем шаге невозможно установить setOnClickListener. это работает для меня. любые отзывы приветствуются

ViewGroup headerView = (ViewGroup) getLayoutInflater().inflate(R.layout.your_header, list,false);
list.addHeaderView(headerView);
headerView.setEnabled(false);
headerView.setOnClickListener(null);
person Ezequiel García    schedule 23.05.2016
comment
Это путь вниз по списку ответов, но это действительно полезно. Если вы не внесете эти изменения, ваш заголовок останется интерактивным, с волновыми эффектами и т. д. - person Scott Ferguson; 05.09.2016

Не для того, чтобы добавить много нового, что @Ramesh не добавил, а для дополнительного контекста:

personList.setOnItemClickListener(new OnItemClickListener() {
    @SuppressWarnings("unchecked")
    public void onItemClick( AdapterView<?> parent, View view, int position, long duration) {
        Map<String, Object> person = (Map<String, Object>) parent.getAdapter().getItem(position);
        if (person != null){

            // If the header was clicked on a null  is returned

            OnPersonUiChangeListener listener = (OnPersonUiChangeListener) getActivity();
            listener.onSelectedPersonChanged(person);
        }
    }
});
person Peter Shannon    schedule 26.11.2013

Используйте getSelectedItemPosition() Возвращает позицию выбранного в данный момент элемента в наборе данных адаптера. Этот метод не должен беспокоиться о размере верхних или нижних колонтитулов.

person luhaiwork    schedule 11.12.2015

Это может помочь кому-то, улучшив ответ Farid_z и Филиппа, мы можем правильно применить setItemChecked и setTitle с чем-то вроде этого

        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
        position += 1;
        mDrawerList.setItemChecked(position, true);
        mDrawerList.setSelection(position);
        position -= 1;
        setTitle(mNavigationDrawerItemTitles[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
person truespan    schedule 09.10.2016