Cómo dibuja vistas Android

Cuando una Activity recibe el foco, se le solicitará que dibuje su diseño. El framework de Android manejará el procedimiento para dibujar, pero la Activity debe proporcionar el nodo raíz de su jerarquía de diseño.

El dibujo comienza con el nodo raíz del diseño. Se solicita que mida y dibuje el árbol de diseño. El dibujo se maneja recorriendo el árbol y representando cada View que se interseca con la región no válida. A su vez, cada ViewGroup es responsable de solicitar que se dibuje cada uno de sus elementos secundarios (con el método draw()), y cada View es responsable de dibujarlo. Debido a que el árbol se recorre por adelantado, los elementos superiores se dibujarán antes (es decir, detrás) de sus elementos secundarios, y los elementos de un mismo nivel se dibujarán en el orden en que aparezcan en el árbol.

Nota: El framework no dibujará objetos View que no se encuentren en una región válida. También se encargará de dibujar el segundo plano de la View por ti.

Puedes hacer que una View dibuje llamando al invalidate().

Dibujar el diseño es un proceso de dos fases: una fase de medida y una de diseño.

Fase de medida

La fase de medida se implementa en measure(int, int) y es un recorrido de arriba abajo del árbol View. Cada View empuja las especificaciones de dimensión hacia abajo en el árbol durante la recursión. Al final de la fase de medida, cada View almacenó sus mediciones. La segunda fase ocurre en layout(int, int, int, int) y también es de arriba abajo. Durante esa fase, cada elemento superior es responsable de ubicar todos sus elementos secundarios utilizando los tamaños procesados en la fase de medida.

Cuando se muestra el método measure() de un objeto View, se deben establecer los valores getMeasuredWidth() y getMeasuredHeight(), junto con los de todos los descendientes de ese objeto View. Los valores medidos de ancho y alto de un objeto View deben respetar las restricciones impuestas por los elementos superiores del objeto View. Eso garantiza que, al final de la fase de medida, todos los elementos superiores acepten todas las medidas de sus elementos secundarios. Un elemento superior View puede llamar a measure() más de una vez en sus elementos secundarios. Por ejemplo, el elemento superior puede medir cada elemento secundario una vez con dimensiones no especificadas para averiguar qué tan grande quiere ser y, luego, volver a llamar a measure() con números reales si la suma de todos los tamaños sin restricciones es demasiado grande o demasiado pequeña (es decir, si los elementos secundarios no se ponen de acuerdo en cuanto al espacio que ocupan, el elemento superior intervendrá y establecerá las reglas en la segunda fase).

La fase de medida utiliza dos clases para comunicar dimensiones. La clase ViewGroup.LayoutParams es utilizada por los objetos View para decirles a sus elementos superiores cómo quieren ser medidos y posicionados. La clase ViewGroup.LayoutParams base solo describe qué tan grande quiere ser View en ancho y alto. Para cada dimensión, puede especificar una de las siguientes opciones:

  • un número exacto
  • MATCH_PARENT, lo que significa que View quiere ser tan grande como su elemento superior (sin incluir el relleno)
  • WRAP_CONTENT, lo que significa que View quiere ser lo suficientemente grande como para encerrar su contenido (más el relleno)

Hay subclases de ViewGroup.LayoutParams para diferentes subclases de ViewGroup. Por ejemplo, RelativeLayout tiene su propia subclase de ViewGroup.LayoutParams, que incluye la capacidad de centrar objetos View secundarios en sentido horizontal y vertical.

Los objetos MeasureSpec se utilizan para empujar los requisitos hacia abajo del árbol, de elemento superior a elemento secundario. Una MeasureSpec puede estar en uno de estos tres modos:

  • UNSPECIFIED: Esto lo utiliza un elemento superior para determinar la dimensión deseada de una View secundaria. Por ejemplo, un LinearLayout puede llamar a measure() en su elemento secundario con la altura establecida en UNSPECIFIED y un ancho de EXACTLY 240 para averiguar qué tan alto quiere que ser la View secundaria si el ancho es de 240 píxeles.
  • EXACTLY: El elemento superior lo usa para imponer un tamaño exacto al elemento secundario. El elemento secundario debe usar este tamaño y garantizar que todos sus descendientes se ajusten a este tamaño.
  • AT MOST: Esto lo usa el elemento superior para imponer un tamaño máximo al elemento secundario. El elemento secundario debe garantizar que él y todos sus descendientes se ajusten a este tamaño.

Fase de diseño

Para iniciar un diseño, llama a requestLayout(). Una View suele llamar a este método para sí misma cuando cree que no puede seguir ajustándose a los límites actuales.