2012-09-24 13 views
23

Tengo un GridView para mostrar algunos iconos.En el adaptador gridview, getView (position == 0) se invocó demasiadas veces para medir el diseño cuando setImageBitmap() en un cargador

Antes de haber leído esta Displaying Bitmaps Efficiently desde el sitio de desarrolladores de Android, que estaba decodificación de mapa de bits de ruta local directamente en getView() del adaptador, como esto:

public View getView(int position, View convertView, ViewGroup parent) { 
     ... 
     ImageView icon = ...... (from getTag() of convertView) 
     icon.setImageBitmap(BitmapUtil.decode(iconPath)); 
     ... 
} 

esta manera funciona bien de todos modos, lo llamé [Modo Directo ], el registro de salida para getView() método debe ser:

getView(0) // measure kid's layout. 
getView(0) 
getView(1) 
getView(2) 
... 
getView(n)  // when scrolling gridview. 
getView(n+1) 
... 
getView(n+3) // scrolling again. 
getView(n+4) 
... 

entonces yo estoy tratando de cambiar el código para [Modo cargador] mencionado en el artículo Displaying Bitmaps Efficiently, de la siguiente manera:

public View getView(int position, View convertView, ViewGroup parent) { 
     ... 
     ImageView icon = ...... (from getTag() of convertView) 
     loadIcon(icon, iconPath); 
     ... 
} 

en loadIcon():

... 
final CacheImageLoader loader = new CacheImageLoader(getActivity(), imageView, imageUrl, savePath); 
final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), placeHolderBitmap, loader); 
imageView.setImageDrawable(asyncDrawable); 

en oyente de bandeja de carga:

@Override 
public void onLoadComplete(Loader<Bitmap> arg0, Bitmap arg1) { 
    ... 
    ImageView imageView = imageViewReference.get(); 
    if (result != null && imageView != null) { 
     imageView.setImageBitmap(result); 
    } 
} 

Básicamente, es el mismo que el código de la formación, en realidad, de esta manera funciona bien también. Sin embargo, encontré algo diferente, en este modo el método getView() en el adaptador se invocaba demasiadas veces, sin embargo, estas llamadas repetidas a este método siempre con el parámetro "posición" == 0, significa algo invocar g etView(0, X, X) repetidamente.

getView(0)  // measure kid's layout. 
getView(0) 
getView(1) 
getView(2) 
...  
getView(0)  // loader completed then imageView.setImageBitmap(result); 
getView(0)  // same as above 
getView(0) 
getView(0) 
... 
getView(n)  // when scrolling gridview. 
getView(n+1) 
getView(n+2) 
getView(0)  // loader completed then imageView.setImageBitmap(result); 
getView(0)  // same as above 
getView(0) 
... 
getView(n+3) // scrolling again. 
getView(n+4) 
getView(0)  // loader completed then imageView.setImageBitmap(result); 
getView(0)  // same as above 
getView(0) 

no es bueno porque estoy utilizando un cargador en getView(). He comprobado el código fuente y se encontró que son llamados originalmente por imageView.setImageBitmap(result) en el método de cargador de onLoadComplete, y en ImageView:

/** 
* Sets a drawable as the content of this ImageView. 
* 
* @param drawable The drawable to set 
*/ 
public void setImageDrawable(Drawable drawable) { 
     ... 

     int oldWidth = mDrawableWidth; 
     int oldHeight = mDrawableHeight; 

     updateDrawable(drawable); 

     if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { 
      requestLayout(); 
     } 
     invalidate(); 
    } 
} 

aquí, requestLayout() es el método de vista y siempre se ejecuta en cualquiera [Modo Directo] o [Modo cargador], en View.class:

public void requestLayout() { 
    mPrivateFlags |= FORCE_LAYOUT; 
    mPrivateFlags |= INVALIDATED; 

    if (mLayoutParams != null) { 
     mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection()); 
    } 

    if (mParent != null && !mParent.isLayoutRequested()) { 
     mParent.requestLayout(); 
    } 
} 

sin embargo, la diferencia es: en [Modo directo], el mParent.requestLayout() se invoca una vez, pero en [Modo cargador], cada momento en el que yo llamo imageView.setImageBitmap(result);, la mParent.requestLayout() será invocado así, significa mParent.isLayoutRequested() retorno false y mParent.requestLayout(); hará que el diseño de su niño GridView medida llamando obtainView() al primer niño y luego causar getView(0, X, X):

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    ... 
    mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); 
    final int count = mItemCount; 
    if (count > 0) { 
     final View child = obtainView(0, mIsScrap); 
    ... 

Por lo tanto, mi pregunta es: ¿por qué mParent.isLayoutRequested() devuelve false si estoy usando [modo de cargador]? o es solo un caso normal?

+0

Por lo que sé que es normal, Android puede llamar a cualquier método de su adaptador en cualquier momento y el tiempo que quiera. Incluso varía de una versión a otra. Nunca asumas nada al respecto. 'GridView' es similar a' ListView' así que echa un vistazo a esta gran charla http://www.youtube.com/watch?v=wDBM6wVEO70. A menos que obtenga un error de bloqueo o de memoria baja, no se preocupe y deje que invoque sus métodos. Y sí, en [modo cargador], Android actualizará GUI más adelante cada vez que se llame a OnLoadFinish(). –

+0

Marca "Configuración> Opciones de desarrollador> Mostrar actualizaciones de diseño" y ejecuta tu aplicación. Si la GUI se soluciona cuando todos sus cargadores están terminados, entonces está listo para continuar. –

+1

¿Encontraste una forma de evitar esto? Yo también entiendo esto. – Matthias

Respuesta

7

isLayoutRequested está allí para decirle si un diseño ya está pendiente para este View. Es decir, después de que se llame al requestLayout, isLayoutRequested devolverá verdadero hasta que se complete el siguiente pase de disposición. La única razón para este control en requestLayout es evitar llamar repetidamente al requestLayout en el elemento primario si de todos modos va a hacer el diseño. isLayoutRequested es un arenque aquí: no es la causa de que se llame al onMeasure repetidamente.

El problema de raíz es que ImageView solicita un nuevo diseño siempre que cambie su dibujable. Esto es necesario por dos razones: -

  1. El tamaño de la ImageView podría depender de la de la estirable, si se establece adjustViewBounds. Esto, a su vez, puede afectar el tamaño de otras vistas, dependiendo del diseño: el ImageView no tiene suficiente información para conocer.
  2. ImageView.onMeasure es responsable de calcular cuánto debe redimensionarse el dibujo para que se ajuste a los límites de ImageView, de acuerdo con el modo de escala. Si el nuevo dibujo no es del mismo tamaño que el anterior, se debe volver a medir el ImageView para volver a calcular la escala requerida.

Solo puede solucionar el problema de tener demasiados cargadores manteniendo un caché local de Bitmap s devuelto por los cargadores. La memoria caché puede tener todos los Bitmap s si sabe que no hay muchos, o solo los n usados ​​más recientemente. En su getView, primero compruebe si el Bitmap para ese artículo existe en la caché, y si es así, devuelva un ImageView ya configurado en ese Bitmap. Solo si no está en la memoria caché, necesita usar un cargador.

Tenga cuidado: si los datos subyacentes pueden cambiar, ahora tiene que asegurarse de invalidar el caché al mismo tiempo que llamar invalidate en el GridView o notificar a través ContentResolver. He usado algunos códigos caseros para lograr esto en mi aplicación, y funciona muy bien para mí, pero los buenos en Square tienen una biblioteca de código abierto llamada Picasso para hacer todo el trabajo por ti, si así lo prefieres.

1

Esto es un comportamiento normal, Android puede llamar a getView para la misma posición varias veces. El desarrollador tiene la función de obtener/establecer miniatura en getView solo cuando sea necesario (es decir, si no se configuró la miniatura o si la ruta de la miniatura tiene cambios). En otros casos simplemente devuelve convertView, que obtenemos como parámetro en getView.

0

intente ajustar su diseño xml en cualquier lugar que haga referencia a la altura, ya que el android hará una medida para dibujar cada vez y volverá a dibujar la celda. Pruebe usar en listview match_parent y en la celda en la fila exactamente la altura. lo siento mi mal inglés.

1

Tuve el mismo problema. Grid siempre mide su primer hijo, incluso si estoy en la posición 30.
acabo puentear todo el código getView mediante la adición de este control en la parte superior getView:

@Override 
public View getView(final int position, View convertView, ViewGroup parent) { 
    // Patch for multiple getView for position 0 
    if(convertView!=null && position==0 && viewGrid.getFirstVisiblePosition()>1) return convertView; 

Esto no impide que getView siendo llamado, pero al menos los textos, las imágenes y el diseño cambia Don' t plazo.

+0

Si puede tener más de un artículo por fila, cambie el "1" con el número máximo de artículos por fila. – Anthony

Cuestiones relacionadas