ListView addHeaderView powoduje wzrost pozycji o jeden?

97

Poniżej znajduje się fragment kodu z ListView. Dodałem emptyView i headerView. Dodanie headerView powoduje zwiększenie pozycji w onItemClick o jeden.

Tak więc bez headerView pierwszy element listy miałby pozycję 0, z nagłówkiem headerView pozycja pierwszego elementu listy byłaby równa 1!

Powoduje to błędy w moim adapterze, np. Podczas wywoływania getItem () i używania innych metod, patrz poniżej. Dziwna rzecz: w metodzie getView () adaptera żądany jest pierwszy element listy z pozycją 0, nawet jeśli dodano 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
        }
    });
}

Niektóre metody adaptera:

@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());
}

Czy jest to normalne zachowanie podczas dodawania headerView? A jak rozwiązać problemy w moim adapterze?

d1rk
źródło
1
Twoje pytanie zawierało odpowiedź na moje pytanie. +1
Shashank Degloorkar

Odpowiedzi:

113

Właśnie natknąłem się na ten problem i wydaje się, że najlepszym sposobem jest użycie ListView.getItemAtPosition(position)zamiast ListAdapter.getItem(position)jako ListViewwersji uwzględniającej nagłówki, tj .: -

Zrób to zamiast tego:

myListView.getItemAtPosition(position)
Ian Warwick
źródło
1
Więc dobrze! onItemClicknie parentbez powodu. Możesz użyćparent.getItemAtPosition(position)
Brais Gabin
3
To faktycznie nie zadziałało dla mnie, podczas gdy użycie adaptera @whuandroid ozdobionego / owijającego działa.
Dori,
3
w tym przypadku musisz także rzucić przedmiot.
njzk2
Nie działa dla mnie, nawet jeśli rzucam rodzica na ListView. Muszę tylko wykluczyć pozycję == 0 ...
PawełP
Nie działa na mnie. AdapterViewrównież przechodzi do dodanego listAdapter. return (adapter == null || position < 0) ? null : adapter.getItem(position);
philipp
31

Jeśli nie zależy Ci na kliknięciu w nagłówek, odejmij liczbę widoków nagłówka od pozycji, aby uzyskać pozycję dla karty:

        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);

        }
    });
farid_z
źródło
To powinna być najlepsza odpowiedź, nie obchodzi mnie nagłówek, zależy mi na liścieitem.
Ninja
Myślę, że to rozwiązanie nie działa w przypadku najnowszego systemu operacyjnego 7.0
sha
27

Jeśli dodasz nagłówek do pozycji, ListViewktóra stanie się pierwszą pozycją na liście. Można to skompensować poprzez nadpisanie getCount()wrócić jeden element mniej, ale upewnij się obsłużyć getItem(), getItemId()i tak dalej jako nagłówek będzie pozycja w pozycji 0.

akshaydashrath
źródło
6
Zastępowanie getCount () w celu zwrócenia jednego elementu mniej nie działa, ponieważ wtedy brakuje mi ostatniego elementu listy. Jeśli na liście jest tylko 1 pozycja, to getView () nigdy nie jest wywoływana. Ponadto adapter jest odłączony od widoku listy i nie wie, czy do widoku listy dodano nagłówek, czy nie.
d1rk
2
W ListItemClick ustaw pozycję = pozycja - 1 jeśli jest dołączony nagłówek, myślę, że to właśnie zrobiłem, gdy miałem do czynienia z tym konkretnym problemem, nie jest to zbyt schludne, ale jedyny sposób, o którym mogę pomyśleć
akshaydashrath
3
Myślę, że najłatwiejszym sposobem byłoby ustawienie position = position- (liczba dodanych headerviews) w OnItemClickListener, jak zasugerowałeś. Zmieniłem swój adapter, aby obsługiwał sam widok głowy, więc nie muszę wywoływać addHeaderView w widoku listy. Chociaż adapter stał się nieco bardziej złożony.
d1rk
22
„Liczbę dodanych wyświetleń nagłówka” można uzyskać pod adresem:ListView.getHeaderViewsCount()
John
2
Nie rozumiem, jak to jest właściwa odpowiedź. @Ian Warwick zamieścił właściwą, choć trochę spóźnioną.
Sidon
14

Podczas implementacji AdapterView.OnItemClickListenerpo prostu odejmujemy nagłówki od pozycji.

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    position -= mListView.getHeaderViewsCount();
    // what ever else follows...
}
philipp
źródło
13

Nie musisz zmieniać żadnego kodu. Po prostu użyj poniższego kodu.

 list.setOnItemClickListener(new OnItemClickListener() {
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      parent.getAdapter().getItem(position);
         //Do something with it
     }
});
Ramesh Akula
źródło
1
Zwykle będzie to wymagało rzucenia, ponieważ zwracany przedmiot to po prostu „Obiekt”.
programista Androida
9

Nie używaj swojego adaptera, użyj „dekorowanego” adaptera z getAdapter.

cycDroid
źródło
+1 ten IMO to droga do zrobienia - dostarczony adapter jest opakowany w inny adapter, który bierze pod uwagę przesunięcie indeksu podczas dostarczania nagłówka. Ten kod dla tego używa to jest przez @Ramesh gdzie indziej na tej stronie (+1 do ciebie też!)
Dori
5

Jednym z rozwiązań jest mapowanie identyfikatora do indeksu. Zasadniczo spraw, aby identyfikator zwrócił indeks na liście, a odbiornik kliknięć zrobiłby:

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            listAdapter.getItem((int) id);
}
Heinrisch
źródło
3

Cześć, możemy rozwiązać ten problem w ten sposób, że najpierw utworzysz widok nagłówka, po wstrzyknięciu do listy, a ostatnim krokiem nie będzie setOnClickListener. to działa dla mnie. wszelkie uwagi mile widziane

ViewGroup headerView = (ViewGroup) getLayoutInflater().inflate(R.layout.your_header, list,false);
list.addHeaderView(headerView);
headerView.setEnabled(false);
headerView.setOnClickListener(null);
Ezequiel García
źródło
Jest daleko na liście odpowiedzi, ale jest to naprawdę przydatne. Jeśli nie wprowadzisz tych zmian, widok nagłówka pozostanie klikalny, z efektami falowania itp.
Scott Ferguson
0

Nie żeby dodać wiele nowego, czego nie dodał @Ramesh, ale dodatkowy kontekst:

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);
        }
    }
});
Peter Shannon
źródło
0

Use getSelectedItemPosition () Zwraca pozycję aktualnie wybranej pozycji w zestawie danych adaptera. Ta metoda nie musi martwić się o rozmiar nagłówków ani stopek.

luhaiwork
źródło
0

Może to komuś pomóc, poprawiając odpowiedź Farid_z i Philippa, możemy poprawnie zastosować setItemChecked i setTitle z czymś takim

        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);
truespan
źródło