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 correctement et l'accent est mis là où il faut aller (avec quelques modifications que j'ai dû coder). Quoi qu’il en soit, mon problème est qu’il existe une condition de concurrence étrange lorsque vous faites défiler la liste avec votre doigt, puis que vous utilisez brusquement la boule de commande lorsque le clavier IME est affiché . Quelque chose doit sortir des limites et être recyclé. A ce stade, la méthode offsetRectBetweenParentAndChild()
doit entrer en action et lancer la IllegalArgumentException
.
Le problème est que cette exception est émise en dehors de tout bloc dans lequel je peux insérer un essai/attraper (autant que je sache). Il y a donc deux solutions valables à cette question, soit:
ViewGroup
mais ces deux méthodes offset*
sont marquées comme finales.Trace de la pile:
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)
Pour ce que ça vaut (ou quiconque trébuche), j'ai abandonné l'approche ListView pour cette activité. Outre les collisions aléatoires, il est presque impossible d'obtenir le comportement de focus correctement sans configurer le windowSoftInputMode="adjustPan"
, qui ouvre de nombreuses autres boîtes de vers. Au lieu de cela, je suis juste allé pour un "simple" ScrollView et cela a fonctionné très bien.
Je suis désolé de vous dire, j'ai trouvé que ma réponse précédente n'était pas le moyen le plus parfait pour résoudre ce problème.
Alors j'essaye ceci:
Ajouter un ScrollListener à votre activité, lorsque listView commence à défiler, efface 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();
}
}
}
}
Tandis que la réponse de Bruce résout le problème, il le fait de manière très brutale, ce qui nuit à l'UX, car cela effacera le focus de chaque vue une fois que nous aurons 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 faites ensuite défiler l'écran jusqu'à ce que le EditText soit hors de l'écran et il n'a pas été recyclé dans 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 il n'est parfois pas nécessaire d'utiliser une vue qui est sortie de l'écran immédiatement afin de la conserver pour une utilisation ultérieure, et parce qu'elle n'a plus besoin d'être affichée. le détachera, entraînant la valeur null de view.mParent. Cependant, le clavier doit savoir comment passer l’entrée et 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 n'a soudainement pas de parent, nous obtenons donc un paramètre "doit être un descendant de cette vue".
En utilisant le listener de défilement, nous causons plus de problèmes.
La solution:
Nous devons écouter un événement qui nous dira quand une vue est partie du 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);
}
}
}
});
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
J'ai rencontré le même problème et découvert cette solution - dans OnGroupCollapseListener/OnGroupExpandListener
et OnScrollListener
pour ExpandableListView
, j'efface le focus et masque le clavier forcé. N'oubliez pas également de définir 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 besoin de adjustResize
dans mon activité au lieu de adjustpan
mais lorsque j'ai essayé, l'erreur s'est produite à nouveau.
J'ai remplacé ScrollView
par <Android.support.v4.widget.NestedScrollView
et tout fonctionne correctement maintenant. J'espère que cela aide quelqu'un!
J'ai également rencontré ce problème et la solution de validcat a fonctionné pour moi, mais j'ai dû appeler getWindow().getCurrentFocus().clearFocus()
.
J'ai rencontré le même problème lorsque j'utilisais EditText
dans Recyclerview
. Après beaucoup de difficultés et d'essais de différentes options, j'ai découvert qu'après la suppression de la ligne lorsque mon clavier est ouvert, ce problème se produit. Je l'ai résolu en forçant la fermeture de mon clavier et en changeant notifyItemRemoved(position)
avec notifyDataSetChanged()
.
Si aucune des solutions suggérées ici ne vous concerne ...
J'ai rencontré une erreur similaire et remarqué qu'elle avait été signalée par les appareils de mes utilisateurs (après un crash) sans aucune explication claire sur sa cause (comme dans le journal indiqué sur la question). Plus précisément, le problème ne concernait que Samsung. Galaxy (y compris S6) (mais pas sur les périphériques Nexus ou autres, c’est pourquoi mes tests n’avaient initialement pas révélé le problème) ... Donc, d’abord, il est utile de vérifier si le problème est spécifique à un périphérique.
Ce que j’ai découvert par la suite, c’est que lorsqu’on appuyait sur le bouton de retour alors qu’un clavier virtuel Samsung était affiché sur un champ de texte, l’application se bloquait, renvoyant cette erreur - mais pas toujours!
En effet, le champ de texte à l’origine du blocage s’affiche également dans une vue de défilement avec fillViewPort = "true" activé.
Ce que j’ai trouvé, c’est que le fait de supprimer l’option fillViewPort de la vue de défilement ne serait pas en conflit avec le clavier Samsung affiché/masqué . Je suppose que le problème est en partie dû au fait que les claviers Samsung sont des claviers virtuels différents des claviers Nexus standard, C’est pourquoi seul un sous-groupe de mes utilisateurs rencontrait le problème et il se bloquerait uniquement sur leurs appareils.
En règle générale, et si aucune des solutions suggérées ne vous concerne, je vérifierai si le problème est spécifique à un appareil, et tenterai aussi de simplifier la vue sur laquelle je travaille jusqu'à ce que je puisse trouver le "composant coupable" (composant J'ajouterais que cela n’a pas été signalé dans les journaux des collisions - je ne suis donc tombé que sur la vue spécifique à l’origine du problème par hasard!).
Désolé de ne pas pouvoir être plus spécifique, mais j'espère que cela vous donnera des pistes pour une enquête plus approfondie si quelqu'un rencontre un problème similaire mais inexpliqué.
Dans mon cas, il était lié à windowSoftInputMode="adjustPan"
, listView et editText sur l'élément list (vue d'en-tête).
Pour résoudre ce problème, j’appelle la méthode de masquage du clavier virtuel 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);
}
}
En cas d'affichage en liste extensible, si vos éléments enfants ont du texte modifié, vous devez modifier la mise au point avant les descendants pour l'affichage en liste extensible
expandableListView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
Ma réponse est liée à la plupart des réponses ici, mais je voulais simplement ajouter que dans mon cas, cet incident est dû à la suppression d'une ligne avec un texte de modification qui avait actuellement le focus.
Donc, tout ce que j'ai fait est de remplacer la méthode de suppression de l'adaptateur et de demander si la ligne supprimée contient l'édition en cours du focus et, le cas échéant, efface le focus.
Cela l'a résolu pour moi.
J'ai la solution la plus simple mais pas bonne. Il suffit d'étendre la méthode NestedScrollView et de remplacer la méthode onSizeChanged, d'ajouter 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 la vue de la couche de remorquage, la couche supérieure est listView, la dernière est NestedScrollView. L'erreur est survenue lorsque je change de couche. La mise au point se fera par élément ListeView (bouton).
Donc, je ne peux pas faire perdre le focus au bouton. La meilleure solution consiste alors à étendre NestedScrollView.
Basé sur @Bruce answer, peut résoudre une 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 de supprimer des éléments.
Ce qui a bien fonctionné a été de surcharger le paramètre "onItemDismiss (int position)" de l'adaptateur, de sorte qu'il effectue d'abord un "notifyDataSetChanged ()" avant de supprimer l'élément, puis "notifyItemRemoved (position)" après le supprimer. 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 un remplacement 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
}