studio que programacion para móviles edición desarrollo desarrollar definicion curso constraint aprende aplicaciones android android-custom-view android-relativelayout onmeasure

que - manual de programacion android pdf



OnMeasure de vista personalizada: cómo obtener ancho basado en altura (2)

La versión anterior de mi pregunta era demasiado prolija. La gente no podía entenderlo, así que lo siguiente es una reescritura completa. Vea el historial de edición si está interesado en la versión anterior.

Un padre de RelativeLayout envía MeasureSpecs al método onMeasure su vista onMeasure para ver qué tan grande le gustaría al niño. Esto ocurre en varios pases.

Mi vista personalizada

Tengo una vista personalizada . A medida que aumenta el contenido de la vista, aumenta la altura de la vista. Cuando la vista alcanza la altura máxima que el padre permitirá, el ancho de la vista aumenta para cualquier contenido adicional (siempre que se wrap_content seleccionado wrap_content para el ancho). Por lo tanto, el ancho de la vista personalizada depende directamente de lo que el padre diga que debe ser la altura máxima.

MeasureSpecs

Una conversación entre padres e hijos (inarmónico)

onMeasure paso 1

El padre de RelativeLayout dice a mi vista, "Puedes tener cualquier ancho hasta 900 y cualquier altura hasta 600 ¿Qué dices?"

Mi opinión dice: "Bueno, a esa altura, puedo colocar todo con un ancho de 100 Así que tomaré un ancho de 100 y una altura de 600 ".

paso de onMeasure 2

El padre de RelativeLayout dice a mi vista, "la última vez que me dijiste que querías un ancho de 100 , así que vamos a establecerlo como un ancho exact . Ahora, según ese ancho, ¿qué tipo de altura te gustaría? Cualquier cosa hasta 500 es DE ACUERDO."

"¡Oye!" Mi opinión responde. "Si solo me das una altura máxima de 500 , entonces 100 es demasiado estrecho. Necesito un ancho de 200 para esa altura. Pero bueno, hazlo a tu manera. No romperé las reglas (aún ... ) . Tomaré un ancho de 100 y una altura de 500 ".

Resultado final

El padre RelativeLayout asigna a la vista un tamaño final de 100 para el ancho y 500 para la altura. Por supuesto, esto es demasiado limitado para la vista y parte del contenido se recorta.

"Suspiro", piensa mi punto de vista. "¿Por qué mis padres no me dejan ser más amplio? Hay mucho espacio. Tal vez alguien en Stack Overflow me pueda dar un consejo".


Para hacer un diseño personalizado, debe leer y comprender este artículo https://developer.android.com/guide/topics/ui/how-android-draws.html

No es difícil implementar el comportamiento que quieres. Solo debe anular onMeasure y onLayout en su vista personalizada.

En onMeasure obtendrá la altura máxima posible de su vista personalizada y call medida () para niños en ciclo. Después de la medición del niño, obtenga la altura deseada de cada niño y calcule si el niño encaja en la columna actual o, si no, aumente la vista personalizada para la nueva columna.

En onLayout, debe llamar a layout () para todas las vistas secundarias para establecer las posiciones dentro del padre. Estas posiciones las has calculado en Medida antes.


Actualización: Código modificado para arreglar algunas cosas.

Primero, permítame decir que usted hizo una gran pregunta y explicó muy bien el problema (¡dos veces!) Aquí está mi solución:

Parece que hay mucho que hacer con onMeasure que, en la superficie, no tiene mucho sentido. Como ese es el caso, dejaremos que onMeasure ejecute y, al final, onMeasure juicio sobre los límites de la View en onLayout estableciendo mStickyWidth en el nuevo ancho mínimo que aceptaremos. En onPreDraw , usando un ViewTreeObserver.OnPreDrawListener , ViewTreeObserver.OnPreDrawListener otro diseño ( requestLayout ). De la documentation (énfasis agregado):

boolean onPreDraw ()

Se invoca el método de devolución de llamada cuando el árbol de vista está a punto de dibujarse. En este punto, todas las vistas en el árbol se han medido y se les ha dado un marco. Los clientes pueden usar esto para ajustar sus límites de desplazamiento o incluso para solicitar un nuevo diseño antes de que ocurra el dibujo .

El nuevo ancho mínimo establecido en onLayout ahora será aplicado por onMeasure que ahora es más inteligente sobre lo que es posible.

He probado esto con su código de ejemplo y parece funcionar bien. Necesitará muchas más pruebas. Puede haber otras formas de hacer esto, pero esa es la esencia del enfoque.

CustomView.java

import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewTreeObserver; public class CustomView extends View implements ViewTreeObserver.OnPreDrawListener { private int mStickyWidth = STICKY_WIDTH_UNDEFINED; public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { logMeasureSpecs(widthMeasureSpec, heightMeasureSpec); int desiredHeight = 10000; // some value that is too high for the screen int desiredWidth; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; // Height if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(desiredHeight, heightSize); } else { height = desiredHeight; } // Width if (mStickyWidth != STICKY_WIDTH_UNDEFINED) { // This is the second time through layout and we are trying renogitiate a greater // width (mStickyWidth) without breaking the contract with the View. desiredWidth = mStickyWidth; } else if (height > BREAK_HEIGHT) { // a number between onMeasure''s two final height requirements desiredWidth = ARBITRARY_WIDTH_LESSER; // arbitrary number } else { desiredWidth = ARBITRARY_WIDTH_GREATER; // arbitrary number } if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else if (widthMode == MeasureSpec.AT_MOST) { width = Math.min(desiredWidth, widthSize); } else { width = desiredWidth; } Log.d(TAG, "setMeasuredDimension(" + width + ", " + height + ")"); setMeasuredDimension(width, height); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int w = right - left; int h = bottom - top; super.onLayout(changed, left, top, right, bottom); // Here we need to determine if the width has been unnecessarily constrained. // We will try for a re-fit only once. If the sticky width is defined, we have // already tried to re-fit once, so we are not going to have another go at it since it // will (probably) have the same result. if (h <= BREAK_HEIGHT && (w < ARBITRARY_WIDTH_GREATER) && (mStickyWidth == STICKY_WIDTH_UNDEFINED)) { mStickyWidth = ARBITRARY_WIDTH_GREATER; getViewTreeObserver().addOnPreDrawListener(this); } else { mStickyWidth = STICKY_WIDTH_UNDEFINED; } Log.d(TAG, ">>>>onLayout: w=" + w + " h=" + h + " mStickyWidth=" + mStickyWidth); } @Override public boolean onPreDraw() { getViewTreeObserver().removeOnPreDrawListener(this); if (mStickyWidth == STICKY_WIDTH_UNDEFINED) { // Happy with the selected width. return true; } Log.d(TAG, ">>>>onPreDraw() requesting new layout"); requestLayout(); return false; } protected void logMeasureSpecs(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); String measureSpecHeight; String measureSpecWidth; if (heightMode == MeasureSpec.EXACTLY) { measureSpecHeight = "EXACTLY"; } else if (heightMode == MeasureSpec.AT_MOST) { measureSpecHeight = "AT_MOST"; } else { measureSpecHeight = "UNSPECIFIED"; } if (widthMode == MeasureSpec.EXACTLY) { measureSpecWidth = "EXACTLY"; } else if (widthMode == MeasureSpec.AT_MOST) { measureSpecWidth = "AT_MOST"; } else { measureSpecWidth = "UNSPECIFIED"; } Log.d(TAG, "Width: " + measureSpecWidth + ", " + widthSize + " Height: " + measureSpecHeight + ", " + heightSize); } private static final String TAG = "CustomView"; private static final int STICKY_WIDTH_UNDEFINED = -1; private static final int BREAK_HEIGHT = 1950; private static final int ARBITRARY_WIDTH_LESSER = 200; private static final int ARBITRARY_WIDTH_GREATER = 800; }