¿Cómo implementar correctamente un listview personalizado con imágenes usando la biblioteca de Picasso?

He creado un diseño personalizado listview con imágenes que se cargan desde la web de esta manera:

http://i.stack.imgur.com/l8ZOc.png

Funciona bien al desplazarse hacia abajo. Sin embargo, cuando se desplaza hacia abajo, los elementos anteriores salen de la pantalla y luego se destruyen. Cuando intenta desplazarse hacia arriba de nuevo, se vuelve a cargar (de caché, más rápido pero no instantáneo) lo que provoca un retraso y no es fluido como debería ser.

1. ¿Hay un ejemplo de cómo hacer esto correctamente?
2.¿Hay una manera de evitar que los elementos listview sean destruidos cuando están fuera de la pantalla?
3.Si es así, ¿causará problemas para mantener demasiados artículos?

Abajo está mi código:

MenuAdapter:

public class MenuAdapter extends BaseAdapter{ Context context; List<MyMenuItem> menuItems; MenuAdapter(Context context, List<MyMenuItem> menuItems) { this.context = context; this.menuItems = menuItems; } @Override public int getCount() { return menuItems.size(); } @Override public Object getItem(int position) { return menuItems.get(position); } @Override public long getItemId(int position) { return menuItems.indexOf(getItem(position)); } private class ViewHolder { ImageView ivMenu; TextView tvMenuHeader; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); if (convertView == null) { convertView = mInflater.inflate(R.layout.menu_item, null); holder = new ViewHolder(); holder.tvMenuHeader = (TextView) convertView.findViewById(R.id.tvMenuHeader); holder.ivMenu = (ImageView) convertView.findViewById(R.id.ivMenuItem); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } MyMenuItem row_pos = menuItems.get(position); Picasso.with(context) .load(row_pos.getItem_image_url()) .into(holder.ivMenu); holder.tvMenuHeader.setText(row_pos.getItem_header()); Log.e("Test", "headers:" + row_pos.getItem_header()); return convertView; } } 

MyMenuItem:

 public class MyMenuItem { private String item_header; private String item_image_url; public MyMenuItem(String item_header, String item_image_url){ this.item_header=item_header; this.item_image_url=item_image_url; } public String getItem_header(){ return item_header; } public void setItem_header(String item_header){ this.item_header=item_header; } public String getItem_image_url(){ return item_image_url; } public void setItem_image_url(String item_image_url){ this.item_image_url=item_image_url; } } 

Actividad principal:

 public class MyActivity extends Activity implements AdapterView.OnItemClickListener { List<MyMenuItem> menuItems; ListView myListView; JSONArray jsonArray; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); Bundle extras = getIntent().getExtras(); if(extras!=null){ try{ jsonArray = new JSONArray(extras.getString("Data")); }catch (Exception e){ e.printStackTrace(); } } menuItems = new ArrayList<MyMenuItem>(); for (int i = 0; i < jsonArray.length(); i++) { try { MyMenuItem item = new MyMenuItem(jsonArray.getJSONObject(i).getString("title"), jsonArray.getJSONObject(i).getString("imageURL")); menuItems.add(item); }catch (Exception e){ e.printStackTrace(); } } myListView = (ListView) findViewById(R.id.list); MenuAdapter adapter = new MenuAdapter(this, menuItems); myListView.setAdapter(adapter); myListView.setOnItemClickListener(this); } } 

MenuItem.xml:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/ivMenuItem" android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="center" android:src="@drawable/em" /> <TextView android:id="@+id/tvMenuHeader" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#55000000" android:paddingBottom="15dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:paddingTop="15dp" android:textColor="@android:color/white" android:layout_gravity="left|top" android:layout_alignBottom="@+id/ivMenuItem" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> </RelativeLayout> 

1. ¿Hay un ejemplo de cómo hacer esto correctamente?

Su código parece bastante cercano a perfecto. El método getView del adaptador es usualmente el camino crítico para optimizar. Compare, por ejemplo, el ejemplo propio de Picasso SampleListDetailAdapter.java . Los puntos importantes que (así como su código) no

  • comprobar y reutilizar las vistas ya infladas, la inflación es cara.
  • use ViewHolder para que no tenga que llamar a findViewById cada vez. No es terriblemente caro en vistas sencillas. También en caché afaik.
  • Picasso.with(context).load(url)... cada vez que necesitas mostrar una imagen. Esto debe terminar al instante, pero todavía utilizan cachés y otra magia.

Hay algunas optimizaciones menores que puede agregar, pero dudo que haya cambios notables o incluso mensurables:

cambio puro del estilo: utilice BaseAdapter#getItem(position) . Este método existe sólo para usted. El marco no lo utiliza.

  @Override public MyMenuItem getItem(int position) { // << subclasses can use subtypes in overridden methods! return menuItems.get(position); } @Override public View getView(int position, View convertView, ViewGroup parent) { ... MyMenuItem row_pos = getItem(position); 

Utilice un método id sano

 @Override public long getItemId(int position) { return menuItems.indexOf(getItem(position)); } 

es equivalente a

 @Override public long getItemId(int position) { return position; } 

pero ahora infinitamente más rápido. indexOf(Object) escala muy mal con el número de objetos.

Objetos de caché que no cambian:

 MenuAdapter(Context context, List<MyMenuItem> menuItems) { this.mLayoutInflater = LayoutInflater.from(content); this.mPicasso = Picasso.with(context); } .. @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.menu_item, null); ... mPicasso .load(row_pos.getItem_image_url()) .into(holder.ivMenu); 

2. ¿Hay alguna manera de evitar que los elementos listview sean destruidos cuando están fuera de la pantalla?

No(*).

.. (*) bien puede esencialmente almacenar en caché el resultado de getView por ejemplo, en LruCache(position, View) o LruCache(MyMenuItem, View) , entonces no toque el convertView – necesitan permanecer no convertidos o matarían esas vistas en su caché. también

 @Override public int getItemViewType(int position) { return Adapter.IGNORE_ITEM_VIEW_TYPE; } 

parecía ser necesario porque el adaptador estándar que utiliza código asume que las vistas que elimina de la visibilidad se han ido. Ellos no son y jugar con los líos con su caché y causado problemas de pantalla extraño para mí.

3. Si es así, ¿causará problemas para mantener demasiados artículos?

Sí. Este comportamiento no es previsto / esperado. También hay más o menos nada que ganar. Es posible que pueda guardar la llamada en holder.tvMenuHeader.setText() . Del mismo modo que el de Picasso pero ambos deben completar instantáneamente. Picasso debería tener su imagen en caché ya. Mediante el almacenamiento en caché de todas las Views se agrega esencialmente otra caché que también contiene todas las imágenes. Prefiero comprobar que la caché picasso funciona como se pretende y contiene la mayoría de los elementos. La única razón por la que puede que desee hacerlo con el caché de vistas es para casos que requieren una configuración complicada de la vista, por lo que se convierte en la pena poner en caché la vista completamente construida en lugar de sólo algunas partes de contenido.

Perfil

La creación de perfiles puede decirle dónde puede / necesita / debe mejorar. El primero en examinar la OMI es traceview. Verá si el código bloquea el subproceso principal, lo que resulta en un desplazamiento de lista intermitente. Si estás haciendo vistas complicadas y ves que los métodos de dibujo se ejecutan la mayor parte del tiempo, perfil de ellos también.

  • ¿Cómo puedo acceder a la imagen en caché de Picasso para hacer una intención de compartir?
  • Solución local de caché de imágenes para Android: Square Picasso vs Universal Image Loader
  • No se puede cargar la imagen desde el disco con picasso
  • Cargar mapa de bits con Picasso
  • Cambiar el tamaño de la imagen a todo el ancho y la altura variable con Picasso
  • Picasso image loading library: cómo cargar miniaturas de video
  • ¿Cómo evitar la reordenación de elementos en StaggeredGridLayoutManager después del cambio de orientación?
  • Rotar correctamente las imágenes web utilizando Picasso de Square
  • Picasso java.lang.IllegalStateException: La llamada al método no debería ocurrir desde el hilo principal
  • Cómo borrar el caché de una URL específica de caché de Picasso
  • Adición de bordes para imagen redondeada android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.