2011-06-09 14 views
9

Respondido¿Se puede omitir Can onMeasure al agregar una vista a un grupo de visualización?

Tengo un RelativeLayout donde estoy añadiendo vistas dinámicamente a medida que el usuario se desplaza verticalmente u horizontalmente. He rodado mi propio ViewRecycler ya que hay potencialmente miles de vistas que podrían componer todo lo que se puede desplazar, pero solo muestro 30 más o menos en cualquier momento. Piensa en un zoom a la vista de un calendario.

Me encuentro con problemas de rendimiento cuando agrego las vistas que están a punto de verse, onMeasure se invoca en RelativeLayout en cascada hasta que onMeasure recibe una llamada en todas sus vistas secundarias. Ya tengo el tamaño calculado de cuán grande será RelativeLayout y lo he configurado en sus parámetros de diseño, por lo que no es necesario medir ViewGroup, ni volver a medir las vistas que ya se han agregado con su tamaño final y el nuevo la vista añadida no tiene relación con esa vista.

El ejemplo simple para demostrar el problema es agregar/eliminar una Vista a una RelativeLayout y ver llamar a onMeasure a pesar de que no afecta el tamaño de RelativeLayout ni la posición de otras Vistas.

main.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/shell" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"> 
    <Button 
     android:id="@+id/button" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content"> 
</LinearLayout> 

MyActivity.java

public class MyActivity extends Activity 
{ 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     ViewGroup shell = (ViewGroup) findViewById(R.id.shell); 

     final RelativeLayout container = new RelativeLayout(this) { 
      @Override 
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
       super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
       Log.d("MyActvity", "onMeasure called on map"); 
      } 
     }; 
     container.setBackgroundColor(Color.rgb(255, 0, 0)); 
     ViewGroup.LayoutParams containerParams = new ViewGroup.LayoutParams(300, 300); 

     final TextView childView = new TextView(this); 
     childView.setBackgroundColor(Color.rgb(0, 255, 0)); 
     childView.setText("Child View"); 

     Button viewToggle = (Button) findViewById(R.id.button); 
     viewToggle.setText("Add/Remove Child View"); 

     viewToggle.setOnClickListener(new View.OnClickListener() { 

      public void onClick(View view) { 
       if (childView.getParent() == null) { 
        container.addView(childView, 400, 30); 
       } else { 
        container.removeView(childView); 
       } 
      } 
     }); 

     shell.addView(container, containerParams); 
    } 
} 

La ejecución de este, que se vería 2 inicial (un esperado) llama a onMeasure, a continuación, una por cada vez que agrega/quita la vista haciendo clic en el botón. Obviamente, esto funciona bien, pero puede ver que las llamadas constantes a onMeasure cuando tiene un diseño complejo de vistas anidadas pueden ser problemáticas.

¿Hay alguna forma recomendada de evitar estas llamadas en Measure o al menos en Measure llamando a measureChildren?

+1

Respondido en android-developers: http://groups.google.com/group/android-developers/browse_frm/thread/5951029333032455 – CommonsWare

+0

Gracias Marque para el enlace cruzado. Actualicé la publicación original con mi solución más rápida/más sucia. –

+1

¿Puedes responder a tu propia pregunta de una manera que pueda ayudar a los demás?Solo edite la "solución" de su pregunta y agréguela como una nueva respuesta. Una vez que lo haga, puede seleccionar el suyo como la respuesta correcta. Puede parecer extraño, pero es la forma preferida de lidiar con situaciones como esta. – Will

Respuesta

0

En lugar de rodar mi propia Controlador de Distribución (que todavía puede hacer en el futuro), que cambió el onMeasure a:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    int count = getChildCount(); 
    for (int i = 0; count > i; i++) { 
     View v = getChildAt(i); 

     if (v.getVisibility() != GONE) { 
      if (v.getMeasuredWidth() <= 0 || v.getMeasuredHeight() <= 0) { 
       measureChild(v, 
        MeasureSpec.makeMeasureSpec(v.getLayoutParams().width, 
         MeasureSpec.AT_MOST), 
        MeasureSpec.makeMeasureSpec(v.getLayoutParams().height, 
         MeasureSpec.AT_MOST)); 
      } 
     } 
    } 

    setMeasuredDimension(resolveSize(staticContainerWidth, widthMeasureSpec), 
     resolveSize(staticContainerHeight, heightMeasureSpec)); 
} 

... y ha añadido una altura codificado sudo-disco y el ancho de la contenedor como una variable. Establecer esto a lo que espera está fuera del alcance de esta solución.

int staticContainerHeight = 300; 
int staticContainerWidth = 300; 
+0

No funciona para mí, toda la vista ni siquiera se muestra. :/ –

+0

Utilice scrollView como vista principal y setFillViewport (true) .. ¡debería funcionar! – AJit

0

me encontré con un problema similar cuando se produce la animación del tamaño de ViewGroup, cuya onMeasure() obtiene la llamada con mucha frecuencia. Debido a que la vista principal contiene numerosas vistas secundarias, las frecuentes llamadas onMeasure() provocaron interrupciones en el rendimiento de la animación. Tengo otra solución sucia pero mucho más simple que implementar mi propio layoutManager.

long mLastOnMeasurTimestamp; 
... 
@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    ... 
    long currentTimestamp = System.currentTimeMillis(); 
    if(currentTimestamp - mLastOnMeasureTimestamp < SKIP_PERIOD_IN_MILL){ 
     return; 
    } 
    mLastOnMeasureTimestamp = currentTimestamp; 
    ... 
0

me he encontrado con un problema similar y mi solución era comprobar si las dimensiones han cambiado:

int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 
setMeasuredDimension(parentWidth, parentHeight); 
if (mClientWidth == parentWidth && mClientHeight == parentHeight) { 
    return; 
} 

mClientWidth = parentWidth; 
mClientHeight = parentHeight; 

Por lo tanto, si las dimensiones de los padres realmente no cambian, no va a ser en cascada hasta sus hijos.

+2

Si agrega un niño a ese padre después de haberlo medido inicialmente, ¿se mide al nuevo niño? Parece que no sería en este caso ... ¿o estoy pasando por alto algo? –

Cuestiones relacionadas