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?
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);
}
});
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);
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())
J'ai eu ce problème dans API Level 17
et résolu en faisant:
listView.clearChoices();
listView.invalidateViews();
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
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);
}
});
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.
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.
J'ai constaté que les deux seules méthodes qui fonctionnent ici (API 19)
sont les suivantes:
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.
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;
}
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.