linearlayout - ¿Es el diseño de Android realmente exponencialmente difícil?
relative layout (4)
Desde aquí: http://developer.android.com/guide/topics/ui/how-android-draws.html
Una vista padre puede llamar a medida () más de una vez en sus hijos. Por ejemplo, el padre puede medir a cada niño una vez con dimensiones no especificadas para averiguar qué tan grande quiere ser, y luego llamar a la medida () nuevamente con números reales si la suma de todos los tamaños no restringidos de los niños es demasiado grande o demasiado pequeña ( es decir, si los niños no están de acuerdo entre ellos en cuanto a la cantidad de espacio que cada uno obtiene, los padres intervendrán y establecerán las reglas en el segundo pase).
Parece que ven el paso de medición como un diálogo entre padres e hijos. En otras palabras, optaron por la máxima flexibilidad en lugar de la optimización. Todavía parece que podrían optimizar los diseños de base sin embargo.
Algunos diseños de androides de uso común, como RelativeLayout y LinearLayout (cuando los pesos no son cero) tienen implementaciones en Medida () que miden a sus hijos dos veces, lo que da como resultado un tiempo de ejecución exponencial cuando se anidan. Esto es fácilmente verificable al emitir las entradas del registro desde onMeasure () de una vista de hoja ... se le llama tiempo de 2 ^ de profundidad.
¿Puede alguien dar una descripción clara y específica de por qué esto es? Y lo que es más importante, ¿este comportamiento exponencial se debe a una parte importante del contrato global o es solo un detalle de la implementación que podría optimizarse? Si se cree que es inevitable, por favor dé un ejemplo que lo requiera.
Este ejemplo me ayudaría enormemente a mí ya otros que se quejan de que el mandato de "mantener sus diseños poco profundos" es oneroso y se preguntan si esto está siendo impulsado simplemente por algoritmos aún no óptimos en las bibliotecas centrales, o si realmente existe Una dificultad fundamental para bloquear una solución.
Tal vez un ejemplo mínimo consistiría en un botón dentro de un LinearLayout dentro de otro LinearLayout (con match_parent y peso = 1 en todas partes, para activar el comportamiento exponencial completo), con algunos parámetros o circunstancias adicionales que dejan en claro que las cuatro llamadas al Botón. onMeasure () son de hecho significativas y necesarias.
Mi primera suposición sería que solo se necesitan dos recorridos de tiempo lineal: el primer recorrido para reunir los tamaños preferidos de todos, el segundo recorrido para distribuir holgura y / o contracción. Otros motores de diseño en el mundo como Tex y Swing parecen ser capaces de manejar rutinariamente jerarquías muy profundas que tienen muchas restricciones de alineación y estiramientos, sin ninguna explosión exponencial, y me imagino que es así como funcionan.
Tenga en cuenta que no quiero respuestas que expliquen cómo se produce el estallido exponencial. Entiendo eso, y ya hubo varias publicaciones donde se ha preguntado y respondido:
- ¿Por qué los pesos anidados son malos para el rendimiento? ¿Alternativas?
- El tiempo de medición del diseño de Android se duplica con cada paso hacia arriba en la jerarquía
- Disposición Advertencia de peso Peso anidado Mal rendimiento
- Eficiencia de la jerarquía de diseño de Android
- http://android-developers.blogspot.com/2009/02/android-layout-tricks-1.html
Mi pregunta es si la doble medición recursiva es fundamentalmente necesaria / justificada, y si es así, me gustaría una explicación / ejemplo claro que muestre por qué.
Editar 2013/8/22: Creo que tal vez mi pregunta todavía no se ha transmitido. Intentaré aclarar y explicar mi motivación, más audazmente esta vez.
El diseño no es un problema exponencialmente difícil, como lo demuestran los motores de diseño eficientes en el mundo como Tex y Swing.
Entonces, ¿qué sucedió con LinearLayout y cómo debería responder la comunidad de desarrolladores de Android? Estoy pidiendo no con el espíritu de culpar, sino más bien a entender y decidir cómo avanzar mejor.
Puedo pensar en 4 posibilidades:
- Corrija el error de rendimiento en la biblioteca central, sin cambiar ningún contrato
- Cambie los contratos según sea necesario y corrija el error de rendimiento en la biblioteca principal
- Escriba una alternativa a LinearLayout, que tiene sus características esenciales (a saber, la distribución de espacio extra entre los niños en proporciones específicas) pero sin el error de rendimiento, y úselo para nuevas aplicaciones
- Continúe microgestionando nuestros diseños para solucionar el error de rendimiento para el resto de nuestras carreras de desarrollo de Android.
(4) no es una opción seria para mí personalmente. Además, me parece claro que cambiar el comportamiento de LinearLayout en este punto no es práctico, por lo que tampoco creo que (2) sea una opción seria.
Eso deja (1) y (3). Soy capaz y estoy dispuesto a hacer cualquiera de esos personalmente, pero ¿cuál? Obviamente (1) es mucho más preferible si es posible, entonces, ¿es posible? Esa parece ser la pregunta de bloqueo crucial que debe ser respondida para determinar cómo avanzar.
He pasado algo de tiempo en el código principal y el documento y no se está aclarando, por eso es que estoy haciendo la pregunta aquí.
El problema en mi opinión es el caché de medida. Hasta donde pude ver, el caché solo funciona si había un diseño de la vista en el medio, por lo que cuando realiza dos medidas consecutivas de un niño (incluso con las mismas especificaciones de medidas exactas) en el mismo "onMeasure" todos los niños y sus sub-hijos se miden de nuevo. Si la memoria caché de medida funcionó bien, la segunda medida debería ser mucho más rápida, ya que tomaría los valores almacenados en caché y no haría que se midiera de nuevo toda la jerarquía de hijos.
En términos de medir a los niños dos veces, entiendo que esto es lo que sucede con LinearLayouts, especialmente cuando se trata de pesos. La mejor explicación que he encontrado para esto proviene de RomainGuy en una de sus presentaciones.
Tiene una diapositiva sobre esto y habla brevemente a las 17:45. Sin embargo, siéntase libre de rebobinar para obtener un poco de contexto. Puede encontrar el video al que estoy haciendo referencia aquí: Devoxx''10 - Dive Into Android
Básicamente, lo que dice es que en la primera pasada calculan el ancho o la altura total según la orientación del LinearLayout, agregan los pesos de los niños y descubren cuánto espacio queda, luego en la segunda pasada con esa información son capaces de repartir correctamente todo el espacio restante a todos los niños. Suficientemente simple.
Sin embargo, también me gustaría señalar que sí, si bien es cierto que las jerarquías de diseño poco profundas tienen menos impacto en el rendimiento, si está agregando solo 1 o 2 capas adicionales, es probable que no vea un gran impacto en el rendimiento. para el usuario. Una vez que se presenta, se hace. Incluso en ListView, si utiliza correctamente el "convertView" dado y configura ViewHolder, obtendrá un buen rendimiento.
Le animo a utilizar DDMS y hacer un volcado de diseño de algunas de las aplicaciones de Google. Son muy complejos y, a menudo, sorprendentemente profundos, pero aún así obtienen un buen rendimiento. No sea estúpido con sus diseños, pero si le ahorra tiempo, agregar un diseño adicional no es el fin del mundo.
Vine aquí esta noche para preguntar esto. Es decepcionante que nadie más parece entender tu pregunta. Después de pensarlo un par de horas más, creo que podría saber la respuesta.
Considera este ejemplo:
El cuadro rojo representa un LinearLayout
con orientación vertical. El cuadro verde representa otro LinearLayout
con orientación horizontal. Espero que el diseño proceda así:
1) El LinearLayout
rojo medirá las alturas de LinearLayout
verde y el widget azul grande en la parte inferior, luego comparará sus pesos, dividirá cualquier espacio vertical sobrante según corresponda, y medirá los hijos nuevamente. (Lo importante que hay que saber aquí es que "medir" una vista realmente establece su tamaño).
2) El LinearLayout
verde medirá los anchos de sus tres hijos, comparará sus pesos, dividirá el espacio horizontal y "medirá" nuevamente.
El problema es que para que el diseño rojo mida la altura del diseño verde, el diseño verde necesita saber la altura de sus hijos.
Ahora, usted pensaría que LinearLayout
sería lo suficientemente inteligente como para optimizar muchos de sus cálculos. Por ejemplo, no hay una razón lógica para que el diseño verde mida el ancho de sus hijos al determinar su propia altura. Y si todos sus hijos tienen una altura de fill_parent
, entonces no debería necesitar realizar ningún cálculo.
Pero la API no permite que LinearLayout
sea tan inteligente. El problema fundamental es que no hay forma de medir solo una dimensión de una vista. Puede obtenerlos por separado después de que se hayan medido, pero la medición real la realiza View # onMeasure (int, int) . Los argumentos de ese método están codificados con View.MeasureSpec , y no hay forma de codificar "ignorar esta dimensión". Así que el LinearLayout
verde LinearLayout
estúpidamente las dos dimensiones de todos sus elementos LinearLayout
cuando el diseño rojo lo mide, y luego repite todo el proceso nuevamente cuando hace su propio diseño. Los anchos no habrán cambiado la segunda vez, pero aún deben recalcularse porque, nuevamente, no hay manera de decirle a la disposición oa sus hijos que midan solo una dimensión.
Entonces, para responder a su pregunta ... no, no necesariamente tiene que ser así, al menos para muchos casos de uso comunes. Esta es una deficiencia de la API de Android.
Si Google agregara nuevos métodos para View
para medir las dimensiones por separado, la implementación predeterminada tendría que depender de onMeasure(int, int)
, que podría ser incluso peor para el rendimiento. Pero si hay algún bit sin usar en la codificación View.MeasureSpec
, entonces podría ser posible agregar un indicador de "ignorar" que permitiría que las futuras versiones de LinearLayout
estén mejor optimizadas.