ListView addHeaderView bewirkt, dass die Position um eins erhöht wird?


97

Unten finden Sie ein Code-Snippet mit einer ListView. Ich habe eine leere Ansicht und eine Header-Ansicht hinzugefügt. Durch Hinzufügen der Header-Ansicht wird die Position im onItemClick um eins erhöht.

Ohne die HeaderView hätte das erste Listenelement also die Position 0, mit der HeaderView wäre die Position des ersten Listenelements 1!

Dies führt zu Fehlern in meinem Adapter, z. B. beim Aufrufen von getItem () und bei Verwendung anderer Methoden (siehe unten). Seltsame Sache: In der getView () -Methode des Adapters wird das erste Listenelement mit Position 0 angefordert, auch wenn die headerView hinzugefügt wird !!

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

Einige Adaptermethoden:

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

Ist dies das normale Verhalten beim Hinzufügen einer Header-Ansicht? Und wie kann ich die Probleme in meinem Adapter lösen?


1
Ihre Frage hatte die Antwort auf meine Frage. +1
Shashank Degloorkar

Antworten:


113

Ich bin gerade auf dieses Problem gestoßen und der beste Weg scheint, das ListView.getItemAtPosition(position)anstelle von ListAdapter.getItem(position)als ListViewVersionskonten für die Header zu verwenden, dh: -

Tun Sie dies stattdessen:

myListView.getItemAtPosition(position)

1
So richtig! onItemClickgibt Ihnen eine parentaus einem Grund. Sie können verwendenparent.getItemAtPosition(position)
Brais Gabin

3
Dies funktionierte bei mir nicht wirklich, wohingegen die Verwendung der @ whuandroid-Adapter- / Wrapper-Adapterlösung dies tut.
Dori

3
auch du musst den Gegenstand in diesem Fall wirken.
NJZK2

Funktioniert bei mir nicht, selbst wenn ich Eltern in ListView umwandle. Ich muss nur Position ausschließen == 0 ...
PawelP

Funktioniert bei mir nicht AdapterViewwird auch nur an den hinzugefügten listAdapter weitergegeben. return (adapter == null || position < 0) ? null : adapter.getItem(position);
Philipp

31

Wenn Sie sich nicht für das Klicken auf die Kopfzeile interessieren, subtrahieren Sie die Anzahl der Kopfzeilenansichten von der Position, um die Position für Ihren Adapter zu erhalten:

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

        }
    });

Dies sollte die beste Antwort sein. Der Header ist mir egal, das Listenelement ist mir wichtig.
Ninja

Ich denke, diese Lösung funktioniert nicht für das neueste OS 7.0
sha

27

Wenn Sie eine Kopfzeile zu einer hinzufügen ListView, wird dies zum ersten Element in der Liste. Man könnte dies kompensieren , indem die übergeordnete getCount()ein Element weniger zurück, aber stellen Sie sicher , den Griff getItem(), getItemId()und so weiter , wie der Header 0 das Element in der Lage sein würde.


6
Das Überschreiben von getCount (), um ein Element weniger zurückzugeben, funktioniert nicht, da ich dann das letzte Listenelement der Liste vermisse. Wenn die Liste nur 1 Element enthält, wird getView () niemals aufgerufen. Außerdem ist der Adapter von der Listenansicht entkoppelt und weiß nicht, ob der Listenansicht ein Header hinzugefügt wurde oder nicht.
d1rk

2
Im ListItemClick setze position = position - 1, wenn ein Header angehängt ist. Ich denke, das habe ich getan, als ich mit diesem speziellen Problem konfrontiert wurde. Es ist nicht sehr ordentlich, aber der einzige Weg, den ich mir
vorstellen

3
Ich denke, der einfachste Weg wäre, position = position- (Anzahl der hinzugefügten Header-Ansichten) im OnItemClickListener zu setzen, wie Sie vorgeschlagen haben. Ich habe meinen Adapter geändert, um die Header-Ansicht selbst zu verwalten, sodass ich addHeaderView in der Listenansicht nicht aufrufen muss. Obwohl der Adapter etwas komplexer wurde.
d1rk

22
Die "Anzahl der hinzugefügten Header-Ansichten" kann erhalten werden mit:ListView.getHeaderViewsCount()
John

2
Ich verstehe nicht, wie das die richtige Antwort ist. @ Ian Warwick hat den richtigen gepostet, allerdings etwas spät, nehme ich an.
Sidon

14

Bei der Implementierung AdapterView.OnItemClickListenersubtrahieren wir nur die Header von der Position.

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

13

Sie müssen keinen Code ändern. Verwenden Sie einfach den folgenden Code.

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

1
Dies muss normalerweise gegossen werden, da der zurückgegebene Gegenstand nur "Objekt" ist.
Android-Entwickler

9

Verwenden Sie Ihren Adapter nicht, sondern den 'dekorierten' Adapter von getAdapter.


+1 Diese IMO ist der richtige Weg - der von Ihnen bereitgestellte Adapter wird mit einem anderen Adapter umwickelt, der den Indexversatz berücksichtigt, wenn Sie einen Header angeben. Dieser Code für diese Verwendung wird von @Ramesh an anderer Stelle auf dieser Seite angegeben (+1 auch für Sie!)
Dori

5

Eine Lösung hierfür besteht darin, die ID dem Index zuzuordnen. Grundsätzlich soll id den Index in der Liste zurückgeben und der Klick-Listener würde Folgendes tun:

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

3

Hallo, wir können dies auf diese Weise beheben. Zuerst erstellen Sie Ihre Header-Ansicht, nachdem Sie Ihre Liste eingefügt haben und der letzte Schritt den setOnClickListener nicht ausführen kann. es funktioniert für mich. Feedback ist willkommen

ViewGroup headerView = (ViewGroup) getLayoutInflater().inflate(R.layout.your_header, list,false);
list.addHeaderView(headerView);
headerView.setEnabled(false);
headerView.setOnClickListener(null);

Es ist ganz unten in der Liste der Antworten, aber das ist wirklich nützlich. Wenn Sie diese Änderungen nicht vornehmen, bleibt Ihre Kopfzeile anklickbar, mit Welligkeitseffekten usw.
Scott Ferguson

0

Nicht viel Neues hinzuzufügen, das @Ramesh nicht hinzugefügt hat, sondern einen zusätzlichen Kontext:

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

0

Verwenden Sie getSelectedItemPosition (). Geben Sie die Position des aktuell ausgewählten Elements im Datensatz des Adapters zurück. Diese Methode muss sich nicht um die Größe der Kopf- oder Fußzeilen kümmern.


0

Dies könnte jemandem helfen und die Antwort von Farid_z und Philipp verbessern. Wir können setItemChecked und setTitle mit so etwas korrekt anwenden

        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);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.