2012-09-04 14 views
263

Intenté hacer un componente personalizado. Extendí la clase View y realicé un dibujo en el método onDraw anulado. ¿Por qué tengo que anular onMeasure? Si no lo hice, todo parece estar bien. ¿Puede alguien explicarlo? ¿Cómo debo escribir mi método onMeasure? He visto dos tutoriales, pero cada uno es un poco diferente que el otro. A veces llaman al super.onMeasure al final, a veces usan setMeasuredDimension y no lo llaman. ¿Dónde está la diferencia?onMeasure vista personalizada explicación

Después de todo quiero usar varios exactamente los mismos componentes. Agregué esos componentes a mi archivo XML, pero no sé cuán grandes deberían ser. Quiero establecer su posición y tamaño más adelante (por qué tengo que establecer el tamaño en onMeasure si está en onDraw cuando lo dibuje, también funciona) en la clase de componente personalizado. ¿Cuándo exactamente necesito hacer eso?

Respuesta

649

onMeasure() es su oportunidad de decirle a Android qué tan grande desea que su vista personalizada dependa de las restricciones de diseño proporcionadas por el padre; también es la oportunidad de su vista personalizada de conocer cuáles son esas restricciones de diseño (en caso de que desee comportarse de manera diferente en una situación match_parent que en una situación wrap_content). Estas restricciones se empaquetan en los valores MeasureSpec que se pasan al método. Aquí está una correlación aproximada de los valores del modo:

  • exactamente significa el valor layout_width o layout_height se establece en un valor específico. Probablemente deberías hacer tu vista de este tamaño. Esto también se puede activar cuando se usa match_parent, para establecer el tamaño exactamente en la vista padre (esto depende del diseño en el marco).
  • AT_MOST normalmente significa el valor layout_width o layout_height se fijó a match_parent o wrap_content donde se necesita un tamaño máximo (esto es el diseño dependiente en el marco), y el tamaño de la dimensión de los padres es el valor. No deberías ser más grande que este tamaño.
  • UNSPECIFIED normalmente significa el valor layout_width o layout_height se fijó a wrap_content sin restricciones. Puede ser del tamaño que desee. Algunos diseños también usan esta devolución de llamada para determinar el tamaño deseado antes de determinar qué especificaciones realmente le pasarán de nuevo en una segunda solicitud de medida.

El contrato que existe con onMeasure() es que setMeasuredDimension()MUST se llamará al final con el tamaño que le gusta la vista que se va. Este método es invocado por todas las implementaciones de infraestructura, incluida la implementación predeterminada que se encuentra en View, por lo que es seguro llamar a super en su lugar, si corresponde a su caso de uso.

Otorgado, porque el marco aplica una implementación predeterminada, puede que no sea necesario que anule este método, pero puede ver el recorte en los casos en que el espacio de vista es menor que su contenido si no lo hace, y si usted diseña su vista personalizada con wrap_content en ambas direcciones, ¡es posible que su vista no se muestre en absoluto porque el marco no sabe qué tan grande es!

general, si está sustituyendo View y no otro widget existente, es probable que sea una buena idea para proporcionar una implementación, incluso si es tan simple como algo parecido a esto:

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

    int desiredWidth = 100; 
    int desiredHeight = 100; 

    int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
    int widthSize = MeasureSpec.getSize(widthMeasureSpec); 
    int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
    int heightSize = MeasureSpec.getSize(heightMeasureSpec); 

    int width; 
    int height; 

    //Measure Width 
    if (widthMode == MeasureSpec.EXACTLY) { 
     //Must be this size 
     width = widthSize; 
    } else if (widthMode == MeasureSpec.AT_MOST) { 
     //Can't be bigger than... 
     width = Math.min(desiredWidth, widthSize); 
    } else { 
     //Be whatever you want 
     width = desiredWidth; 
    } 

    //Measure Height 
    if (heightMode == MeasureSpec.EXACTLY) { 
     //Must be this size 
     height = heightSize; 
    } else if (heightMode == MeasureSpec.AT_MOST) { 
     //Can't be bigger than... 
     height = Math.min(desiredHeight, heightSize); 
    } else { 
     //Be whatever you want 
     height = desiredHeight; 
    } 

    //MUST CALL THIS 
    setMeasuredDimension(width, height); 
} 

espero que ayude.

+1

Hola @Devunwired buena explicación, la mejor que he leído hasta ahora. Tu explicación respondió muchas de las preguntas que tuve y despejó algunas dudas, pero aún queda una que es: si mi vista personalizada está dentro de un grupo de visualización junto con algunas otras vistas (no importa qué tipos) ese grupo de visualización obtendrá una ¿cada uno prueba su restricción de LayoutParams y le pide a cada niño que se mida a sí mismo de acuerdo con sus limitaciones? – pharaoh

+0

Sí, eso es lo que hace el método 'measureChildren()' de 'ViewGroup' durante el proceso de medida/diseño. – Devunwired

+34

Tenga en cuenta que este código no funcionará si anula onMeasure de cualquier subclase de ViewGroup. Sus subvistas no se mostrarán y todas tendrán un tamaño de 0x0. Si necesita sobrescribir en Measurement of a Custom ViewGroup, cambie widthMode, widthSize, heightMode y heightSize, compílelos de nuevo para measureSpecs utilizando MeasureSpec.makeMeasureSpec y pase los enteros resultantes a super.onMeasure. – Alexey

4

Actualmente, su respuesta no es completa, ya que los valores también dependen del contenedor de embalaje. En el caso de diseños relativos o lineales, los valores se comportan como este:

  • exactamente match_parent es exactamente + tamaño de la matriz
  • AT_MOST resultados wrap_content en un AT_MOST MeasureSpec
  • UNSPECIFIED Nunca activado

En caso de una vista de desplazamiento horizontal, su código funcionará.

+49

Si considera que alguna respuesta aquí es incompleta, por favor agréguela en lugar de dar una respuesta parcial. –

+0

Bueno para vincular esto con cómo funcionan los diseños, pero en mi caso onMeasure se llama tres veces para mi vista personalizada. La vista en cuestión tenía una altura wrap_content y un ancho ponderado (ancho = 0, peso = 1).La primera llamada tenía NO ESPECIFICADA/NO ESPECIFICADA, la segunda tenía AT_MOST/EXACTLY y la tercera tenía EXACTAMENTE/EXACTAMENTE. –

Cuestiones relacionadas