web-dev-qa-db-fra.com

La sélection ListView reste persistante après la sortie du mode choix

J'ai une sous-classe ListView sur laquelle j'autorise les sélections lorsque la barre d'action contextuelle (CAB) est active. Le CAB est défini comme un rappel de l'événement onItemLongClick:

public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    // Inflate a menu resource providing context menu items
    MenuInflater inflater = mode.getMenuInflater();
    inflater.inflate(context_menu, menu);
    getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    return true;
}

Cela convient et ListView fonctionne comme prévu, l'élément actuellement sélectionné restant en surbrillance lorsque vous le touchez.

Lorsque je ferme le CAB, je veux que le ListView revienne à la normale (c.-à-d. Le mode tactile) . Le problème est que le dernier élément sélectionné reste en surbrillance indéfiniment, quelles que soient les méthodes que j'essaie de supprimer:

public void onDestroyActionMode(ActionMode mode) {
    //Unselect any rows
    ListView lv = getListView();
    lv.clearChoices(); // Has no effect
    lv.setChoiceMode(ListView.CHOICE_MODE_NONE); // Has no effect on the highlighted item 
    lv.setFocusable(false); // Has no effect
    lv.setSelection(0); // Has no effect
    mActionMode = null;
}

Aucune suggestion?

41
CjS

La principale raison du problème est qu’une fois que le mode de sélection ListView est passé à CHOICE_MODE_NONE, la structure optimise l’opération clear car elle ne prend plus en charge les 'sélections'. J'ai un peu amélioré les solutions de contournement ci-dessus en effaçant manuellement l'état de sélection, puis en définissant le mode de manière différée afin que le cadre puisse effacer l'état avant de passer le mode à CHOICE_MODE_NONE.

final ListView lv = getListView();
lv.clearChoices();
for (int i = 0; i < lv.getCount(); i++)
    lv.setItemChecked(i, false);
lv.post(new Runnable() {
    @Override
    public void run() {
        lv.setChoiceMode(ListView.CHOICE_MODE_NONE);
    }
});
34
Rudi

J'ai été confronté au même problème et, puisque demander la mise en page ne résout pas le problème pour moi, j'ai également mis en place un petit bidouillage qui fonctionne pour moi. Peut-être que c'est le même problème parce que je bascule entre CHOICE_MODE_SINGLE et CHOICE_MODE_NONE.

Lorsque le mode action se termine, j'appelle cet extrait de code. clearChoices s'assure que tous les éléments ne sont plus vérifiés (en interne). L'itération sur les vues permet de s'assurer que toutes les vues actuellement visibles sont réinitialisées et non vérifiées.

mListView.clearChoices();

for (int i = 0; i < mListView.getChildCount(); i++) {
    ((Checkable) mListView.getChildAt(i)).setChecked(false);
}

mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
19
Knickedi

En regardant le code source ListView, le seul moyen de contourner ce problème consiste à définir ListView sur CHOICE_MODE_NONE, puis à réaffecter le ListAdapter (qui efface la liste de sélection interne quel que soit le mode de choix).

c'est-à-dire dans un ListFragment/ListActivity

getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
getListView().setAdapter(getListAdapter())
18
Migaloo

J'ai eu ce problème dans API Level 17 et résolu en faisant:

listView.clearChoices();
listView.invalidateViews();
4
jpmcosta

Pour moi, il semble que la réponse acceptée ne fonctionne pas pour les éléments invisibles, et il n'est pas nécessaire d'appeler

for (int i = 0; i < lv.getCount(); i++)
        lv.setItemChecked(i, false);

à la place, il suffit d'appeler

lv.requestLayout();

Pour résoudre complètement mon problème, j'appelle 

lv.clearChoices();
lv.requestLayout();

dans onDestroyActionMode()

et appeler

lv.setItemChecked(position, false)

dans onItemClick() quand ce n'est pas dans ActionMode

Cependant, je n'ai pas confirmé si l'appel setItemChecked() entraînerait des problèmes de performances

2
darktiny

Cela a été consigné en tant que bogue AOSP , mais marqué comme obsolète pour une raison quelconque.

Normalement, vous vous attendriez à ce que cela fonctionne:

getListView().clearChoices();
getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);

Malheureusement ce n'est pas le cas. Le fait de différer le mode de choix des paramètres à aucun lors de la prochaine passe de disposition fonctionnerait:

getListView().clearChoices();
getListView().post(new Runnable() {
    @Override
    public void run() {
        getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
    }
});
1
hidro

Je sais que la réponse à cette question a été trouvée, mais les réponses ci-dessus me posaient tout de même des problèmes avec les vues en cache/recyclées conservées par ListView, qui ne mettaient pas à jour son état lors du défilement dans la vue . Ainsi, la solution ci-dessus passe légèrement à:

    lv.clearChoices();  

    ArrayList<View> list = new ArrayList<View>();
    lv.reclaimViews(list);
    for (View view : list) {
        ((Checkable) view).setChecked(false);
    }

    lv.setChoiceMode(lv.CHOICE_MODE_NONE);

C'est mieux que d'utiliser getChildAt (i) car cette méthode jusg vous donne les vues actuellement visibles et ne tient pas compte des vues internes mises en cache, qui ne sont pas visibles.

0
anjosc

Pas sûr que ce soit trop tard, je voulais juste partager. J'ai créé une intention sur la même page afin qu'une fois les données cliquées capturées, celle-ci recrée une nouvelle page sans aucune persistance cliquée. 

0
Critical_Eye

J'ai constaté que les deux seules méthodes qui fonctionnent ici (API 19) sont les suivantes:

  • Réinitialisation de l'adaptateur de liste, ce qui n'est pas souhaitable car il revient en haut de la liste; 
  • Définition du mode de choix sur CHOICE_MODE_NONE dans un new Runnable

Si le mode de choix est modifié sans utiliser listView.post(new Runnable()), cela ne fonctionne pas. Quelqu'un peut-il m'expliquer pourquoi c'est? 

Toutes mes excuses pour ne pas commenter; Je n'ai aucune réputation 

Merci.

0
mdhvn

J'avais essayé toutes les approches décrites ci-dessus mais aucune d'entre elles ne fonctionnait pour moi. Enfin, je décide d'appliquer la solution de contournement suivante. L'idée clé est que, 

En multimode, au lieu de réutiliser la vue "mise en cache", nous allons créer une vue complètement nouvelle. Pas efficace, mais au moins "partiellement" résoudre mon problème.

Voici le code de ma ArrayAdapter personnalisée

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // Key to solve this problem. When we are in multimode, we will not reusing the cached view.
    View rowView = this.multimode ? null : convertView;

    if (rowView == null) {
        LayoutInflater inflater = activity.getLayoutInflater();
        rowView = inflater.inflate(R.layout.watchlist_row_layout, null);
        ViewHolder viewHolder = new ViewHolder();
        viewHolder.textView0 = (TextView) rowView.findViewById(R.id.text_view_0);
        viewHolder.textView1 = (TextView) rowView.findViewById(R.id.text_view_1);
        viewHolder.textView2 = (TextView) rowView.findViewById(R.id.text_view_2);
        rowView.setTag(viewHolder);
    }

De plus, je me sens plus en sécurité d’avoir le code suivant dans ActionMode.Callback, bien que je ne sache pas à quel point cela aide.

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        MyFragment.this.myArrayAdapter.setMultimode(false);

        // http://stackoverflow.com/questions/9754170/listview-selection-remains-persistent-after-exiting-choice-mode
        // Using View.post is the key to solve the problem.
        final ListView listView = MyFragment.this.getListView();
        listView.clearChoices();
        for (int i = 0, ei = listView.getChildCount(); i < ei; i++) {
            listView.setItemChecked(i, false);
        }
        listView.post(new Runnable() {
            @Override
            public void run() {
                listView.setChoiceMode(ListView.CHOICE_MODE_NONE);
            }
        });
        actionMode = null;
    }

Note latérale

L'utilisation de couple MultiChoiceModeListener avec CHOICE_MODE_MULTIPLE_MODAL supprimera ce bogue. Cependant, pour les appareils de niveau inférieur à l'API, le niveau 11 ne pourra pas utiliser cette solution.

0
Cheok Yan Cheng