ListView addHeaderView provoque une augmentation de la position de un?


97

Vous trouverez ci-dessous un extrait de code avec un ListView. J'ai ajouté un emptyView et un headerView. L'ajout de headerView entraîne une augmentation de la position dans onItemClick.

Donc, sans headerView, le premier élément de la liste aurait la position 0, avec l'en-têteView la position du premier élément de la liste serait 1!

Cela provoque des erreurs dans mon adaptateur, par exemple lors de l'appel de getItem () et de l'utilisation d'autres méthodes, voir ci-dessous. Chose étrange: dans la méthode getView () de l'adaptateur, le premier élément de liste est demandé avec la position 0 même si l'en-têteView est ajouté !!

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

Quelques méthodes d'adaptateur:

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

Est-ce le comportement normal lors de l'ajout d'un headerView? Et comment surmonter les problèmes de mon adaptateur?


1
Votre question avait la réponse à ma question. +1
Shashank Degloorkar

Réponses:


113

Je viens de tomber sur ce problème et la meilleure façon semble d'utiliser le ListView.getItemAtPosition(position)au lieu de ListAdapter.getItem(position)car la ListViewversion tient compte des en-têtes, c'est-à-dire: -

Faites ceci à la place:

myListView.getItemAtPosition(position)

1
Tellement vrai! onItemClickvous donne parentune raison. Vous pouvez utiliserparent.getItemAtPosition(position)
Brais Gabin

3
Cela n'a pas fonctionné pour moi alors que l'utilisation de la solution d'adaptateur décoré / wrapper @whuandroid fonctionne.
Dori

3
vous devez également lancer l'élément dans ce cas.
njzk2

Cela ne fonctionne pas pour moi même si je lance le parent vers ListView. J'ai juste besoin d'exclure la position == 0 ...
PawelP

Ça ne marche pas pour moi. AdapterViewpasse également simplement au listAdapter ajouté. return (adapter == null || position < 0) ? null : adapter.getItem(position);
philipp

31

Si vous ne vous souciez pas du clic sur l'en-tête, soustrayez le nombre de vues d'en-tête de la position pour obtenir la position de votre adaptateur:

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

        }
    });

Cela devrait être la meilleure réponse, je ne me soucie pas de l'en-tête, je me soucie de la liste.
Ninja

Je pense que cette solution ne fonctionne pas pour le dernier OS 7.0
sha

27

Si vous ajoutez un en-tête à un, ListViewcela devient le premier élément de la liste. Vous pouvez compenser cela en remplaçant le getCount()pour renvoyer un élément de moins, mais assurez-vous de gérer le getItem(), getItemId()et ainsi de suite car l'en-tête serait l'élément en position 0.


6
Le remplacement de getCount () pour renvoyer un élément de moins ne fonctionne pas, car alors je manque le dernier élément de la liste. S'il n'y a qu'un seul élément dans la liste, getView () n'est jamais appelé. De plus, l'adaptateur est découplé de la vue de liste et ne sait pas si un en-tête a été ajouté à la vue de liste ou non.
d1rk

2
Dans le ListItemClick set position = position - 1 s'il y a un en-tête attaché, je pense que c'est ce que j'ai fait quand j'ai fait face à ce problème particulier, ce n'est pas très soigné, mais la seule façon dont je peux penser
akshaydashrath

3
Je suppose que le moyen le plus simple serait de définir position = position- (nombre de headerviews ajoutés) dans OnItemClickListener comme vous l'avez suggéré. J'ai changé mon adaptateur pour gérer la vue d'ensemble elle-même, donc je n'ai pas besoin d'appeler addHeaderView sur la liste. Bien que l'adaptateur soit devenu un peu plus complexe.
d1rk

22
Le "nombre de headerviews ajoutés" peut être obtenu avec:ListView.getHeaderViewsCount()
John

2
Je ne vois pas en quoi c'est la bonne réponse. @Ian Warwick a posté le bon, même si je suppose un peu tard.
sidon

14

Lors de la mise en œuvre, AdapterView.OnItemClickListenernous soustrayons simplement les en-têtes de la position.

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

13

Pas besoin de changer de code. Utilisez simplement le code ci-dessous.

 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
Cela nécessitera généralement un casting, car l'élément renvoyé est simplement "Objet".
développeur android

9

N'utilisez pas votre adaptateur, utilisez l'adaptateur «décoré» de getAdapter.


+1 cet IMO est la voie à suivre - l'adaptateur que vous fournissez est emballé avec un autre adaptateur qui prend en compte le décalage d'index lorsque vous fournissez un en-tête. Ce code pour utiliser ceci est donné par @Ramesh ailleurs sur cette page (+1 pour vous aussi!)
Dori

5

Une solution à cela consiste à mapper l'id à l'index. Fondamentalement, faites que id renvoie l'index dans la liste et l'écouteur de clic ferait:

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

3

Salut, nous pouvons résoudre cela de cette manière, vous créez d'abord votre vue d'en-tête, après l'injection dans votre liste et la dernière étape impossible le setOnClickListener. ses travaux pour moi. toute rétroaction bienvenue

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

C'est en bas de la liste des réponses, mais c'est vraiment utile. Si vous ne faites pas ces changements, alors votre vue d'en-tête restera cliquable, avec des effets d'entraînement, etc.
Scott Ferguson

0

Pas pour ajouter beaucoup de nouveautés que @Ramesh n'a pas ajoutées, mais un contexte supplémentaire:

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

Use getSelectedItemPosition () Renvoie la position de l'élément actuellement sélectionné dans l'ensemble de données de l'adaptateur. Cette méthode n'a pas besoin de se soucier de la taille des en-têtes ou des pieds de page.


0

Cela pourrait aider quelqu'un, en améliorant la réponse de Farid_z et Philipp, nous pouvons correctement appliquer setItemChecked et setTitle avec quelque chose comme ça

        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);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.