android - Evitar/capturar el error "IllegalArgumentException: el parámetro debe ser un descendiente de esta vista"
viewgroup viewroot (14)
Tengo un ListView con algunos componentes EditText
dentro (sobre todo EditText
s). Sí, sé que esto no se recomienda exactamente, pero en general, casi todo está funcionando bien y el enfoque va donde tiene que ir (con algunos ajustes que tuve que codificar). De todos modos, mi problema es que hay una condición de carrera extraña al desplazar la lista con el dedo y, de repente, utilizar la bola de seguimiento cuando se muestra el teclado IME . Algo debe salir de los límites y reciclarse, momento en el que el método offsetRectBetweenParentAndChild()
debe IllegalArgumentException
y lanzar IllegalArgumentException
.
El problema es que esta excepción se produce fuera de cualquier bloque en el que puedo insertar un try / catch (hasta donde sé). Entonces, hay dos soluciones válidas para esta pregunta, ya sea:
- Alguien sabe por qué se lanza esta excepción y cómo evitar que suceda
- Alguien sabe cómo poner un bloque de prueba / captura en algún lugar que al menos permita que mi aplicación sobreviva. Hasta donde yo sé, el problema es el enfoque, por lo que definitivamente no debería matar a mi aplicación (que es lo que está haciendo). Traté de sobreescribir los métodos de
ViewGroup
, pero esos dos métodos deoffset*
están marcados como finales.
Stack trace:
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)
En el caso de la Vista de lista expandible, si sus elementos secundarios tienen texto de edición, entonces necesita cambiar la capacidad de enfoque antes de los descendientes para la Vista de lista desplegable.
expandableListView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
En mi caso, estaba relacionado con windowSoftInputMode="adjustPan"
, listView y editText on list element (vista de encabezado).
Para arreglar eso llamo método de teclado oculto antes de que la actividad termine.
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);
}
}
Estoy usando RecyclerView y ninguna de las soluciones presentadas funcionó. Obtuve el error al eliminar elementos.
Lo que hizo el trabajo fue anular el ''onItemDismiss (int position)'' del adaptador para que primero haga un ''notifyDataSetChanged ()'' antes de eliminar el elemento y luego ''notifyItemRemoved (position)'' después de eliminar el elemento. Me gusta esto:
// 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);
}
}
También haga una anulación de ''removeAt (int position)'' en el TabFragment para invocar el nuevo código de limpieza, como este:
// TabFragment code
@Override
public void removeAt(int position) {
mAdapter.onItemDismiss(position);
mAdapter.notifyItemRemoved(position); // <--- I put an extra notify here too
}
Lamento decirte, encontré que mi respuesta anterior no es la forma más perfecta de resolver este problema.
Entonces intento esto:
Agregue un ScrollListener a su actividad, cuando listView comience a desplazarse, borre el enfoque actual.
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();
}
}
}
}
Me enfrenté a ese problema, también, y la solución de validcat funcionó para mí, pero tuve que llamar a getWindow().getCurrentFocus().clearFocus()
.
Me enfrenté al mismo problema cuando uso EditText
en Recyclerview
. Después de mucha lucha y probar diferentes opciones, descubrí que después de eliminar la fila cuando se abre el teclado produce este problema. Lo resolví a la fuerza cerrando mi teclado y cambiando notifyItemRemoved(position)
con notifyDataSetChanged()
.
Me enfrenté al mismo problema y descubrí esta solución: en OnGroupCollapseListener/OnGroupExpandListener
y OnScrollListener
para ExpandableListView
OnGroupCollapseListener/OnGroupExpandListener
y el teclado forzado. Además, no olvides configurar el manifest
para tu actividad 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) {}
});
No sé exactamente si OnGroupExpandListener
es necesario o no, podría ser inútil.
Mi respuesta está relacionada con la mayoría de las respuestas aquí, pero solo quería agregar que en mi caso, este bloqueo se produjo debido a la eliminación de una fila con un texto de edición que actualmente tenía el foco.
Así que todo lo que hice fue anular el método de eliminación del adaptador, y me pregunté si la fila eliminada contiene la edición de enfoque actual y, de ser así, borre el foco.
Eso lo resolvió para mí.
Por lo que vale (o quienquiera que tropiece con esto), he abandonado el enfoque ListView para esta actividad. Además de los bloqueos aleatorios, es casi imposible obtener el comportamiento de enfoque correctamente sin configurar windowSoftInputMode="adjustPan"
que abre un montón de otras latas de gusanos. En cambio, opté por un ScrollView "simple" y eso ha funcionado muy bien.
Según la respuesta de @Bruce, puede resolver el error con recyclerview de esta manera:
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View currentFocus = ((Activity)context).getCurrentFocus();
if (currentFocus != null) {
currentFocus.clearFocus();
}
}
Si bien la respuesta de Bruce resuelve el problema, lo hace de una manera muy brutal que perjudica al UX, ya que borrará el foco de cada vista una vez que hicimos un desplazamiento.
Se trata del síntoma del problema, pero no resuelve la causa real.
cómo reproducir el problema:
Su EditText tiene foco y se abre el teclado, luego se desplaza hasta el punto donde EditText está fuera de la pantalla, y no se recicló a un nuevo EditText que ahora se muestra.
Primero, comprendamos por qué ocurre este problema:
ListView recicla sus vistas y las utiliza de nuevo como todos saben, pero a veces no necesita utilizar una vista que se ha salido de la pantalla de forma inmediata, por lo que la mantiene para su uso futuro y, como ya no es necesario mostrarla, lo separará haciendo que view.mParent sea nulo. sin embargo, el teclado necesita saber cómo pasar la entrada a, y lo hace eligiendo la vista enfocada, o EditText para ser preciso.
Entonces, el problema es que tenemos un EditText que tiene el foco, pero de repente no tiene un elemento principal, por lo que obtenemos el error "el parámetro debe ser un descendiente de esta vista". Tiene sentido.
Al usar el detector de desplazamiento, estamos causando más problemas.
La solución:
Necesitamos escuchar un evento que nos dirá cuándo una vista se ha ido a un lado y ya no está adjunta, afortunadamente ListView expone este evento.
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);
}
}
}
});
Si ninguna de las soluciones sugeridas aquí se aplica a usted ...
Experimenté un error similar y me di cuenta de que los dispositivos de mis usuarios lo habían informado (después de un bloqueo) sin una explicación clara de lo que estaba causando (igual que el registro que se muestra en la pregunta). Más específicamente, el problema solo ocurrió en Samsung. Dispositivos Galaxy (incluido S6) (pero no en dispositivos Nexus u otros, por lo que mis pruebas inicialmente no revelaron el problema). Entonces, primero, vale la pena verificar si el problema es específico del dispositivo o no.
Lo que más tarde descubrí es que al presionar el botón Atrás mientras se visualizaba un teclado virtual de Samsung en un campo de texto, la aplicación colgaba arrojando este error, ¡pero no siempre!
De hecho, el campo de texto que causaba el bloqueo también se mostró en una vista de desplazamiento con fillViewPort = "true" habilitado.
Lo que encontré es que quitar la opción fillViewPort de la vista de desplazamiento no entraría en conflicto con el teclado Samsung que se muestra / oculta. Sospecho que el problema se debe en parte al hecho de que los teclados Samsung son teclados virtuales diferentes que los teclados Nexus estándar, por lo que solo un subconjunto de mis usuarios estaba experimentando el problema y se colgaría solo en sus dispositivos.
Como regla general, y si ninguna de las otras soluciones sugeridas aquí se aplican a usted, verificaría si el problema es específico del dispositivo, y también intentaré simplificar la vista en la que estoy trabajando hasta que pueda encontrar el "componente culpable" (componente y ver que, debo agregar, no fue reportado en los registros de fallas, ¡así que solo tropecé con la vista específica que causaba el problema por casualidad!).
Lo siento, no puedo ser más específico, pero espero que esto proporcione algunos indicadores para una mayor investigación si alguien experimenta un problema similar pero inexplicable.
Usé la respuesta de bruce con un ligero ajuste.
Necesitaba adjustResize en mi actividad en lugar de ajustar pero cuando lo intenté, el error ocurrió nuevamente.
ScrollView
con <android.support.v4.widget.NestedScrollView
y funciona bien ahora. ¡Espero que esto ayude a alguien!
prueba esto
@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
}
EDITAR:
Ver también: Mejor solución