2011-09-14 8 views
84

¿Cuál es la forma correcta de anular onMeasure()? He visto varios enfoques. Por ejemplo, Professional Android Development usa MeasureSpec para calcular las dimensiones y luego finaliza con una llamada a setMeasuredDimension(). Por ejemplo:manera automática de reemplazar onMeasure()?

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ 
int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 
this.setMeasuredDimension(parentWidth/2, parentHeight); 
} 

Por otro lado, como por this post, la forma "correcta" es utilizar MeasureSpec, llame setMeasuredDimensions(), seguido de una llamada a setLayoutParams(), y terminando con una llamada a super.onMeasure(). Por ejemplo:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ 
int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 
this.setMeasuredDimension(parentWidth/2, parentHeight); 
this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight)); 
super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
} 

Entonces, ¿cuál es la forma correcta? Ninguno de los enfoques ha funcionado al 100% para mí.

Creo que realmente lo que estoy preguntando es si alguien sabe de un tutorial que explica onMeasure(), el diseño, las dimensiones de las vistas de los niños, etc.?

+15

¿Le pareció útil/correcta la respuesta? ¿por qué no marcarlo como * la * respuesta? – superjos

Respuesta

38

La documentación es la autoridad en este asunto: http://developer.android.com/guide/topics/ui/how-android-draws.html y http://developer.android.com/guide/topics/ui/custom-components.html

En resumen: al final de su método reemplazado onMeasure debe llamar setMeasuredDimension.

No debe llamar al super.onMeasure después de llamar al setMeasuredDimension, que simplemente borrará lo que haya configurado. En algunas situaciones, es posible que desee llamar al super.onMeasure primero y luego modificar los resultados llamando al setMeasuredDimension.

No llame al setLayoutParams en onMeasure. El diseño ocurre en un segundo pase después de la medición.

+0

Mi experiencia es que si anulo onMeasure sin llamar a super.onMeasure, OnLayout no se llama en vistas secundarias. –

-1

Supongo que setLayoutParams y volver a calcular las medidas es una solución alternativa para cambiar el tamaño de las vistas secundarias correctamente, ya que esto generalmente se hace en onMeasure de la clase derivada.

Sin embargo, esto rara vez funciona correctamente (por la razón que sea ...), invoque mejor measureChildren (al derivar un ViewGroup) o intente algo similar cuando sea necesario.

63

Las otras soluciones no son completas. Pueden funcionar en algunos casos, y son un buen lugar para comenzar, pero no se garantiza que funcionen.

Cuando se llama a onMeasure, puede o no tener los derechos para cambiar el tamaño. Los valores que se pasan a su onMeasure (widthMeasureSpec, heightMeasureSpec) contienen información sobre lo que su vista hija puede hacer. Actualmente hay tres valores:

  1. MeasureSpec.UNSPECIFIED - Puede ser tan grande como desee
  2. MeasureSpec.AT_MOST - Tan grande como desee (hasta el tamaño de especificaciones), se trata de parentWidth en su ejemplo.
  3. MeasureSpec.EXACTLY - No hay otra opción. El padre ha elegido.

Esto se hace para que Android pueda realizar múltiples pases para encontrar el tamaño correcto para cada artículo, consulte here para obtener más detalles.

Si no sigue estas reglas, no se garantiza que su enfoque funcione.

Por ejemplo, si desea comprobar si se le permite cambiar el tamaño en todo lo que puede hacer lo siguiente:

final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); 
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); 
boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; 
boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; 

Con esta información usted sabrá si puede modificar los valores que en tu codigo. O si se requiere que hagas algo diferente. Una forma rápida y fácil de resolver su tamaño deseado es utilizar uno de los métodos siguientes:

int resolveSizeAndState (int size, int measureSpec, int childMeasuredState)

int resolveSize (int size, int measureSpec)

Mientras que la primera sólo está disponible en nido de abeja, la segunda es disponible en todas las versiones de.

Nota: Puede encontrar que resizeWidth o resizeHeight son siempre falsos. Descubrí que este es el caso si estaba solicitando MATCH_PARENT. Pude solucionar esto solicitando WRAP_CONTENT en mi diseño principal y luego durante la fase SIN ESPECIFICAR solicitando un tamaño de Integer.MAX_VALUE. Si lo haces, obtendrás el tamaño máximo que tus padres permiten en el próximo pase en Medida.

-4

se puede tomar esta pieza de código como un ejemplo de onMeasure() ::

public class MyLayerLayout extends RelativeLayout { 

    public MyLayerLayout(Context context) { 
     super(context); 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
     int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 

     int currentChildCount = getChildCount(); 
     for (int i = 0; i < currentChildCount; i++) { 
      View currentChild = getChildAt(i); 

      //code to find information 

      int widthPercent = currentChildInfo.getWidth(); 
      int heightPercent = currentChildInfo.getHeight(); 

//considering we will pass height & width as percentage 

      int myWidth = (int) Math.round(parentWidth * (widthPercent/100.0)); 
      int myHeight = (int) Math.round(parentHeight * (heightPercent/100.0)); 

//Considering we need to set horizontal & vertical position of the view in parent 

      AlignmentTraitValue vAlign = currentChildInfo.getVerticalLocation() != null ? currentChildlayerInfo.getVerticalLocation() : currentChildAlignmentTraitValue.TOP; 
      AlignmentTraitValue hAlign = currentChildInfo.getHorizontalLocation() != null ? currentChildlayerInfo.getHorizontalLocation() : currentChildAlignmentTraitValue.LEFT; 
      int topPadding = 0; 
      int leftPadding = 0; 

      if (vAlign.equals(currentChildAlignmentTraitValue.CENTER)) { 
       topPadding = (parentHeight - myHeight)/2; 
      } else if (vAlign.equals(currentChildAlignmentTraitValue.BOTTOM)) { 
       topPadding = parentHeight - myHeight; 
      } 

      if (hAlign.equals(currentChildAlignmentTraitValue.CENTER)) { 
       leftPadding = (parentWidth - myWidth)/2; 
      } else if (hAlign.equals(currentChildAlignmentTraitValue.RIGHT)) { 
       leftPadding = parentWidth - myWidth; 
      } 
      LayoutParams myLayoutParams = new LayoutParams(myWidth, myHeight); 
      currentChildLayoutParams.setMargins(leftPadding, topPadding, 0, 0); 
      currentChild.setLayoutParams(myLayoutParams); 
     } 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    } 
} 
+5

Este código es fundamentalmente incorrecto. En primer lugar, no tiene en cuenta el modo de diseño (ver la respuesta de Grimmace). Eso es malo, pero no verá el efecto de eso porque también llama a super.onMeasure() al final, que anula los valores que establece (vea la respuesta de satur9nine) y anula el objetivo de anular onMeasure(). – spaaarky21

0

Si se cambia el tamaño de la vista en el interior onMeasure todo lo que necesita es la llamada setMeasuredDimension. Si cambia el tamaño fuera de onMeasure, debe llamar al setLayoutParams. Por ejemplo, cambiar el tamaño de una vista de texto cuando se cambia el texto.

+0

Todavía puede llamar a setMinWidth() y setMaxWidth() y tener el estándar onMeasure manejarlo. He descubierto que es mejor no llamar a setLayoutParams desde dentro de una clase de vista personalizada. Realmente se usa para ViewGroups o Activities para anular el comportamiento de vista de niño. – Dorrin

0

aquí es cómo he resuelto el problema:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 

     .... 

     setMeasuredDimension(measuredWidth, measuredHeight); 

     widthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY); 
     heightMeasureSpec = MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY); 

     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

}

Además era necesario que el componente ViewPager

+2

Esta no es la solución adecuada. 'super.onMeasure' borrará las dimensiones establecidas usando' setMeasuredDimension'. – tomrozb

+0

@tomrozb no es http://www.humptydevelopers.com/2013/05/android-view-overriding-onmeasure.html y puede consultar las fuentes de Android –

0

Creo que depende de los padres lo que están anulando.

Por ejemplo, si se amplía un ViewGroup (como FrameLayout), cuando se ha medido el tamaño, debe llamar, como a continuación

super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), 
       MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 

porque es posible que desee ViewGroup para hacer el trabajo resto (hacer algo cosas en la vista de niño)

Si está extendiendo una Vista (como ImageView), puede simplemente llamar al this.setMeasuredDimension(width, height);, porque la clase de padres hará algo como lo ha hecho habitualmente.

En pocas palabras, si quiere algunas características que su clase para padres ofrece gratis, debe llamar al super.onMeasure() (pase MeasureSpec.EXACTLY mode measure spec por lo general), de lo contrario llame al this.setMeasuredDimension(width, height); es suficiente.

Cuestiones relacionadas