Empêcher / intercepter «IllegalArgumentException: paramètre doit être un descendant de cette vue»

J’ai un ListView avec quelques composants focalisables à l’intérieur (principalement EditText s). Oui, je sais que ce n’est pas exactement recommandé, mais en général, presque tout fonctionne bien et l’accent est mis là où il faut aller (avec quelques modifications à faire). Quoi qu’il en soit, mon problème est qu’il ya une condition de course bizarre lorsque vous faites défiler la liste avec votre doigt puis que vous utilisez soudainement la boule de commande lorsque le clavier IME est affiché . Quelque chose doit sortir des limites et être recyclé à quel point la méthode offsetRectBetweenParentAndChild() doit IllegalArgumentException en jeu et lancer l’ IllegalArgumentException .

Le problème est que cette exception est lancée en dehors de tout bloc dans lequel je peux insérer un try / catch (pour autant que je sache). Il y a donc deux solutions valables à cette question, soit:

  1. Quelqu’un sait pourquoi cette exception est lancée et comment l’empêcher de se produire
  2. Quelqu’un sait comment placer un bloc try / catch qui permettra au moins à mon application de survivre. Pour autant que je sache, le problème est celui du focus, donc il ne faut absolument pas tuer mon application (ce qui est ce qu’elle fait). J’ai essayé de ViewGroup les méthodes du ViewGroup mais ces deux méthodes offset* sont marquées comme étant finales.

Trace de la stack:

 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): FATAL EXCEPTION: main 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): java.lang.IllegalArgumentException: parameter must be a descendant of this view 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at android.view.ViewGroup.offsetRectBetweenParentAndChild(ViewGroup.java:2633) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at android.view.ViewGroup.offsetDescendantRectToMyCoords(ViewGroup.java:2570) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at android.view.ViewRoot.scrollToRectOrFocus(ViewRoot.java:1624) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at android.view.ViewRoot.draw(ViewRoot.java:1357) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at android.view.ViewRoot.performTraversals(ViewRoot.java:1258) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at android.view.ViewRoot.handleMessage(ViewRoot.java:1859) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at android.os.Handler.dispatchMessage(Handler.java:99) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at android.os.Looper.loop(Looper.java:130) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at android.app.ActivityThread.main(ActivityThread.java:3683) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at java.lang.reflect.Method.invokeNative(Native Method) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at java.lang.reflect.Method.invoke(Method.java:507) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 08-17 18:23:09.825: ERROR/AndroidRuntime(1608): at dalvik.system.NativeStart.main(Native Method) 

Je suis désolé de vous le dire, j’ai trouvé que ma réponse précédente n’est pas le moyen le plus parfait pour résoudre ce problème.

Donc j’essaye ceci:
Ajoutez un ScrollListener à votre activité, lorsque listView commence à défiler, effacez le focus actuel.

 protected class MyScrollListener implements OnScrollListener { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // do nothing } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (SCROLL_STATE_TOUCH_SCROLL == scrollState) { View currentFocus = getCurrentFocus(); if (currentFocus != null) { currentFocus.clearFocus(); } } } } 

essaye ça

  @Override public View getView(int position, View convertView, ViewGroup parent) { //abandon current focus View currentFocus = ((Activity)mContext).getCurrentFocus(); if (currentFocus != null) { currentFocus.clearFocus(); } // other code } 

MODIFIER:

Voir aussi: Meilleure solution

Bien que la réponse de Bruce résout le problème, elle le fait d’une manière très brutale qui nuit à l’UX, car elle permettra de clarifier le sharepoint vue de chaque vue une fois que nous avons fait défiler.

Il traite du symptôme du problème mais ne résout pas la cause réelle.

comment reproduire le problème:

Votre EditText a le focus et le clavier est ouvert. Vous pouvez ensuite faire défiler le texte jusqu’à ce que le texte EditText ne soit plus affiché et qu’il ne soit pas recyclé vers un nouveau EditText qui est maintenant affiché.

Comprenons d’abord pourquoi ce problème se produit:

ListView recycle ses vues et les utilise à nouveau comme vous le savez tous, mais parfois il n’a pas besoin d’utiliser une vue qui s’est éteinte immédiatement de l’écran pour le conserver pour une utilisation ultérieure et parce qu’il n’a plus besoin de l’afficher le détachera, provoquant la nullité de view.mParent. Cependant, le clavier doit savoir comment transmettre l’entrée, et il le fait en choisissant la vue focalisée ou EditText pour être précis.

Le problème est donc que nous avons un EditText qui a le focus, mais qu’il n’a soudainement pas de parent, donc nous obtenons une erreur “paramètre doit être un descendant de cette vue”.

En utilisant l’écouteur de défilement, nous causons plus de problèmes.

La solution:

Nous avons besoin d’écouter un événement qui nous dira quand une vue est passée sur le tas et n’est plus attachée, heureusement ListView expose cet événement.

 listView.setRecyclerListener(new AbsListView.RecyclerListener() { @Override public void onMovedToScrapHeap(View view) { if ( view.hasFocus()){ view.clearFocus(); //we can put it inside the second if as well, but it makes sense to do it to all scraped views //Optional: also hide keyboard in that case if ( view instanceof EditText) { InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } } } }); 

Pour ce que cela vaut (ou quiconque trébuche sur ce point), j’ai abandonné l’approche ListView pour cette activité. Mis à part les accidents aléatoires, il est presque impossible d’obtenir le comportement de focus correctement sans définir le windowSoftInputMode="adjustPan" qui ouvre un tas d’autres boîtes de vers. Au lieu de cela, je suis juste allé pour un “simple” ScrollView et cela a bien fonctionné.

J’ai rencontré le même problème et j’ai découvert cette solution – dans OnGroupCollapseListener/OnGroupExpandListener et OnScrollListener pour ExpandableListView j’ai effacé le focus et masqué le clavier forcé. N’oubliez pas non plus de mettre en manifest pour votre activité windowSoftInputMode="adjustPan" :

  expListView.setOnGroupCollapseListener(new OnGroupCollapseListener() { @Override public void onGroupCollapse(int groupPosition) { InputMethodManager inputManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE); if (getWindow().getCurrentFocus() != null) { inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); getCurrentFocus().clearFocus(); } } }); expListView.setOnGroupExpandListener(new OnGroupExpandListener() { @Override public void onGroupExpand(int groupPosition) { InputMethodManager inputManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE); if (getWindow().getCurrentFocus() != null) { inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); getCurrentFocus().clearFocus(); } } }); expListView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { InputMethodManager inputManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE); if (getCurrentFocus() != null) { inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); getCurrentFocus().clearFocus(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {} }); 

Je ne sais pas exactement si OnGroupExpandListener est nécessaire ou non, cela pourrait être inutile.

J’ai utilisé la réponse de bruce avec un léger ajustement.

J’avais plutôt besoin de adjustResize dans mon activité sur ajustpan mais lorsque je l’ai essayé, l’erreur s’est de nouveau produite.
J’ai remplacé ScrollView par et cela fonctionne très bien maintenant. J'espère que cela aide quelqu'un!

J’ai aussi rencontré ce problème et la solution de validcat a fonctionné pour moi, mais j’ai dû appeler getWindow().getCurrentFocus().clearFocus() .

Dans mon cas, il était lié à windowSoftInputMode="adjustPan" , listView et editText on list element (vue d’en-tête).

Afin de résoudre ce problème, j’appelle masquer la méthode du clavier logiciel avant la fin de l’activité.

 public void hideKeyboard(Activity activity) { InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE); View focusView = activity.getCurrentFocus(); if (focusView != null) { inputMethodManager.hideSoftInputFromWindow(focusView.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } } 

Si aucune des solutions suggérées ici ne vous concerne …

J’ai rencontré une erreur similaire et j’ai remarqué qu’elle était signalée par les appareils de mes utilisateurs (après une panne) sans explication claire de ce qui la provoquait (identique au journal affiché sur la question). Galaxy (y compris les appareils S6) (mais pas sur les appareils Nexus ou autres, c’est pourquoi mes tests n’ont pas réussi à révéler le problème). Donc, tout d’abord, il convient de vérifier si le problème est spécifique à l’appareil ou non.

Ce que j’ai trouvé plus tard, c’est que lorsque vous appuyez sur le bouton de retour alors qu’un clavier virtuel Samsung était affiché sur un champ de texte, l’application plantait cette erreur, mais pas toujours!

En effet, le champ de texte à l’origine du crash était également affiché dans une scrollview avec fillViewPort = “true” activé.

Ce que j’ai trouvé, c’est que la suppression de l’option fillViewPort de la vue de défilement n’entre pas en conflit avec le clavier Samsung affiché / masqué. Je soupçonne que le problème est en partie dû au fait que les claviers Samsung sont des claviers virtuels différents de ceux des claviers Nexus, ce qui explique pourquoi seul un sous-ensemble de mes utilisateurs rencontrait ce problème et qu’il se bloquait uniquement sur leurs appareils.

En règle générale, et si aucune des solutions suggérées ici ne s’applique à vous, je vérifierais si le problème est spécifique à l’appareil, et aussi pour simplifier la vue sur laquelle je travaille jusqu’à ce que je puisse trouver le “composant coupable” (composant et voir que, je devrais append, n’a pas été signalé dans les journaux de plantage – donc je suis seulement tombé sur la vue spécifique causant le problème par hasard!).

Désolé je ne peux pas être plus précis, mais j’espère que cela donne quelques indications pour une enquête plus approfondie si quelqu’un éprouve un problème similaire mais inexpliqué.

Dans le cas d’une vue de liste extensible, si vos éléments enfants ont du texte de modification, vous devez modifier la capacité de mise au point avant les descendants pour la vue de liste extensible.

 expandableListView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); 

J’ai rencontré le même problème lors de l’utilisation de EditText dans Recyclerview . Après de nombreuses difficultés et en essayant différentes options, j’ai découvert que la suppression de la ligne lorsque mon clavier est ouvert produit ce problème. Je l’ai résolu en notifyItemRemoved(position) fermeture de mon clavier et en changeant notifyItemRemoved(position) avec notifyDataSetChanged() .

Ma réponse est liée à la plupart des réponses ici, mais je voulais juste append que dans mon cas, cet incident est survenu en raison de la suppression d’une ligne avec un texte de modification qui avait actuellement le focus.

Donc, tout ce que j’ai fait, c’est remplacer la méthode remove de l’adaptateur et demander si la ligne supprimée contient la modification de focus en cours et si oui, effacer le focus.

Cela l’a résolu pour moi.

Basé sur la réponse de @Bruce, peut résoudre l’erreur avec recyclerview comme ceci:

 @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View currentFocus = ((Activity)context).getCurrentFocus(); if (currentFocus != null) { currentFocus.clearFocus(); } } 

J’utilise RecyclerView et aucune des solutions présentées n’a fonctionné. J’ai eu l’erreur lors de la suppression des éléments.

Ce qui a fonctionné a été de remplacer le paramètre “onItemDismiss (int position)” de l’adaptateur pour qu’il commence par “notifyDataSetChanged ()” avant de supprimer l’élément, puis qu’il fasse “notifyItemRemoved (position)” après avoir supprimé l’élément. Comme ça:

 // Adapter code @Override public void onItemDismiss(int position) { if (position >= 0 && getTheList() != null && getTheList().size() > position) { notifyDataSetChanged(); // <--- this fixed it. getTheList().remove(position); scrollToPosition(position); notifyItemRemoved(position); } } 

Faites également une substitution de 'removeAt (int position)' dans TabFragment pour appeler le nouveau code de nettoyage, comme ceci:

 // TabFragment code @Override public void removeAt(int position) { mAdapter.onItemDismiss(position); mAdapter.notifyItemRemoved(position); // <--- I put an extra notify here too } 

J’ai la solution la plus simple mais pas bonne. Étendez simplement la méthode NestedScrollView et remplacez la méthode onSizeChanged, ajoutez un bloc try catch.

 public class FixFocusErrorNestedScrollView extends NestedScrollView { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { try { super.onSizeChanged(w, h, oldw, oldh); } catch (Exception e) { e.printStackTrace(); } }} 

Dans mon cas, j’ai une vue de couche de remorquage, la couche supérieure est listView, en bas est NestedScrollView. L’erreur se produit lorsque je change de couche. Le focus doit être pris par l’élément ListeView (bouton).

Je ne peux donc pas faire perdre le focus au bouton. La meilleure solution est alors NestedScrollView.