ListView addHeaderView fa aumentare la posizione di uno?


97

Di seguito è riportato uno snippet di codice con ListView. Ho aggiunto un emptyView e un headerView. L'aggiunta di headerView fa aumentare di uno la posizione in onItemClick.

Quindi senza headerView il primo elemento della lista avrebbe la posizione 0, con headerView la posizione del primo elemento della lista sarebbe 1!

Ciò causa errori nel mio adattatore, ad esempio quando si chiama getItem () e si utilizzano altri metodi, vedere di seguito. Cosa strana: nel metodo getView () dell'adattatore viene richiesto il primo elemento della lista con posizione 0 anche se viene aggiunto 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
        }
    });
}

Alcuni metodi di adattamento:

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

È questo il comportamento normale quando si aggiunge un headerView ?? E come superare i problemi nel mio adattatore?


1
La tua domanda aveva la risposta alla mia domanda. +1
Shashank Degloorkar

Risposte:


113

Mi sono appena imbattuto in questo problema e il modo migliore sembra usare il ListView.getItemAtPosition(position)invece di ListAdapter.getItem(position)poiché la ListViewversione tiene conto delle intestazioni, ovvero: -

Fai questo invece:

myListView.getItemAtPosition(position)

1
Così giusto! onItemClickti dà parentun motivo. Puoi usareparent.getItemAtPosition(position)
Brais Gabin

3
Questo in realtà non ha funzionato per me mentre usando la soluzione dell'adattatore decorato / wrapper @whuandroid lo fa.
Dori

3
anche in questo caso devi lanciare l'oggetto.
njzk2

Non funziona per me anche se lancio genitore su ListView. Devo solo escludere la posizione == 0 ...
PawelP

Non funziona per me. AdapterViewpassa anche solo all'elencoAdapter aggiunto. return (adapter == null || position < 0) ? null : adapter.getItem(position);
philipp

31

Se non ti interessa il clic sull'intestazione, sottrai il numero di visualizzazioni dell'intestazione dalla posizione per ottenere la posizione dell'adattatore:

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

        }
    });

Questa dovrebbe essere la risposta migliore, non mi interessa l'intestazione, mi interessa l'elemento della lista.
Ninja

Credo che questa soluzione non funziona per l'ultimo OS 7.0
sha

27

Se aggiungi un'intestazione a una, ListViewquesta diventa la prima voce dell'elenco. Potresti compensare questo sovrascrivendo getCount()per restituire un articolo in meno, ma assicurati di gestire il getItem(), getItemId()e così via poiché l'intestazione sarebbe l'elemento nella posizione 0.


6
L'override di getCount () per restituire un elemento in meno non funziona, perché allora mi manca l'ultimo elemento dell'elenco. Se c'è un solo elemento nell'elenco, getView () non viene mai chiamato. Inoltre l'adattatore è disaccoppiato da listview e non sa se è stata aggiunta un'intestazione a listview oppure no.
d1rk

2
In ListItemClick imposta position = position - 1 se c'è un'intestazione allegata, penso che sia quello che ho fatto quando ho affrontato questo particolare problema, non è molto pulito, ma l'unico modo in cui posso pensare
akshaydashrath

3
Immagino che il modo più semplice sarebbe impostare position = position- (numero di headerviews aggiunti) in OnItemClickListener come suggerito. Ho cambiato il mio adattatore per gestire lo stesso headerview, quindi non devo chiamare addHeaderView su listview. Anche se l'adattatore è diventato un po 'più complesso.
d1rk

22
Il "numero di headerviews aggiunti" può essere ottenuto con:ListView.getHeaderViewsCount()
John

2
Non vedo come questa sia la risposta giusta. @Ian Warwick ha pubblicato quello giusto, anche se un po 'in ritardo presumo.
Sidon

14

Durante l'implementazione AdapterView.OnItemClickListener, sottraiamo semplicemente le intestazioni dalla posizione.

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

13

Non è necessario modificare alcun codice. Usa semplicemente il codice sottostante.

 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
Di solito è necessario eseguire il cast, poiché l'articolo restituito è solo "Object".
sviluppatore Android

9

Non utilizzare l'adattatore, utilizzare l'adattatore "decorato" di getAdapter.


+1 questo IMO è la strada da percorrere: l'adattatore che fornisci è avvolto con un altro adattatore che tiene conto dell'offset dell'indice quando fornisci un'intestazione. Questo codice per questo sta usando questo è dato da @Ramesh altrove in questa pagina (+1 anche a te!)
Dori

5

Una soluzione a questo problema è mappare l'id su index. Fondamentalmente fai in modo che id restituisca l'indice nell'elenco e il listener di clic farebbe:

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

3

Salve, possiamo risolvere questo problema in questo modo prima crei la visualizzazione dell'intestazione, dopo aver inserito nella tua lista e l'ultimo passaggio non è in grado di impostare setOnClickListener. per me funziona. qualsiasi feedback benvenuto

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

È in fondo alla lista delle risposte, ma è davvero utile. Se non apporti queste modifiche, la visualizzazione dell'intestazione rimarrà selezionabile, con effetti a catena, ecc.
Scott Ferguson,

0

Non per aggiungere molte novità che @Ramesh non ha aggiunto ma un po 'di contesto in più:

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

Usa getSelectedItemPosition () Restituisce la posizione dell'elemento attualmente selezionato nel data set dell'adattatore. Questo metodo non deve preoccuparsi della dimensione delle intestazioni o dei piè di pagina.


0

Questo potrebbe aiutare qualcuno, migliorando la risposta di Farid_z e Philipp, possiamo applicare correttamente setItemChecked e setTitle con qualcosa di simile

        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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.