¿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
- La biblioteca de Picasso no carga imágenes de la tarjeta SD en Android
- Cargando imágenes con Picasso desde el almacenamiento interno
- ¿Cómo obtengo el mapa de bits resultante de una imagen después de haber sido redimensionado en una vista de imagen con picasso
- Java.lang.IllegalArgumentException: La ruta no debe estar vacía en Picasso
- Google Map personaliza la imagen de marcador con picasso, se actualiza dos veces para mostrar la imagen
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>
- Cultivo de Picasso a una vista
- Elementos compartidos de Android con Picasso
- Preload de imágenes en la memoria / disco con Android Picasso
- Picasso no carga imagen de caché en listview
- Picasso no puede cargar imágenes para alguna URL (sin caracteres especiales)
- Android Picasso - Cómo obtener estadísticas sobre accesos y descargas de caché
- ¿Cómo hacer deslizar la pantalla como picasso?
- Picasso y PhotoView Library cargan imagen en ImageView extraño
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 afindViewById
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.
- Error al ejecutar git "No se puede ejecutar el programa" git ": error = 2
- ¿Se instalará manualmente apk (no desde la tienda de reproducción) cuando la actualización esté disponible?