Categories: Android

Adapter con carga asíncrona de imágenes (Smooth ListView)

En este post vamos a explicar cómo hacer una correcta implementación de un ListView con un CustomAdapter en el que cada elemento contiene imágenes que se descargarán de manera asíncrona como en la app de Whatsapp.

Vamos a utilizar para el desarrollo del CustomAdapter el patrón ViewHolder con el fin de reutilizar las vistas y que nuestra ListView de un look and feel muy bueno y fluido.

El código del proyecto se podrá descargar desde este enlace:

POCImageAdapter

Antes de nada vamos a crear nuestra ListView en XML y luego vamos a referenciarla desde el código de nuestra Activity, a continuación se puede ver el código de la Activity y todo explicado en los comentarios.

public class MainActivity extends Activity implements OnItemClickListener {

  private ListView mListViewItems;
  private CustomAdapter mAdapter;
  private List mListCategories = new ArrayList();
  private String[] mUrls = {};
  private Random mRandom = new Random();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

  getViews();
    init();
  }

  private void getViews() {

    mListViewItems = (ListView) findViewById(R.id.listView__activity_main_items);
  }

  private void init() {

    mUrls = getResources().getStringArray(R.array.urls);

    mListViewItems.setOnItemClickListener(this);

    for(int i = 0; i < 50; i++)
      mListCategories.add(new ModelCategory("Item " + i, mUrls[mRandom.nextInt(mUrls.length - 1)]));

    mAdapter = new CustomAdapter(MainActivity.this, mListCategories);
    mListViewItems.setAdapter(mAdapter);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

  @Override
  public boolean onMenuItemSelected(int featureId, MenuItem item) {

    switch (item.getItemId()) {
      case R.id.action_reset:

        mListCategories = new ArrayList();
        break;
      case R.id.action_add:

        mListCategories.add(new ModelCategory("Object added", mUrls[mRandom.nextInt(mUrls.length - 1)]));
        break;
      case R.id.action_remove:

        if(mListCategories.size() > 0)
          mListCategories.remove(mListCategories.size() - 1);
        break;
      default:
        break;
    }

    mAdapter.setDataset(mListCategories);
    mAdapter.notifyDataSetChanged();

    return true;
  }

  @Override
  public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {

    Toast.makeText(MainActivity.this, mListCategories.get(position).getTitle(), Toast.LENGTH_SHORT).show();
  }

}

Una vez realizado todo esto, vamos a crear nuestro CustomAdapter que extiende de BaseAdapter. Además lo haremos con el patrón ViewHolder que conseguirá reutilizar las vistas de las celdas. Código con comentarios de CustomAdapter:

public class CustomAdapter extends BaseAdapter{

  private List mDataset;
  private LayoutInflater mLayoutInflater;
  private Context mContext;

  public class ViewHolder {

    public ImageView logo;
    public TextView title;
  }

  public CustomAdapter(Context context, List items) {

    mContext = context;
    mLayoutInflater = LayoutInflater.from(context);
    mDataset = items;
  }

  public void setDataset(List newDataset) {

    mDataset = newDataset;
  }

  @Override
  public int getCount() {
    return mDataset.size();
  }

  @Override
  public ModelCategory getItem(int position) {
    return mDataset.get(position);
  }

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

  @Override
  public View getView(final int position, View convertView, ViewGroup parent) {

    ViewHolder holder;
    if(convertView == null) {
      convertView = mLayoutInflater.inflate(R.layout.simple_category_list_item, null);

      holder = new ViewHolder();
      holder.logo = (ImageView) convertView.findViewById(R.id.imageView__simple_category_list_item_logo);
      holder.title = (TextView) convertView.findViewById(R.id.textView__simple_category_list_item_title);
      convertView.setTag(holder);

    } else {

      holder = (ViewHolder)convertView.getTag();
    }

    Picasso.with(mContext).load(getItem(position).getUrl()).into(holder.logo);

    holder.title.setText(getItem(position).getTitle());

    return convertView;
  }
}

Como se puede ver, utilizamos la librería Picasso para la carga asíncrona de imágenes, dicha librería es muy fácil de añadir. Simplemente tenemos que incluir el jar descargable desde la web en la carpeta “libs” y luego con el botón derecho “Build Pathy” –>  “Add to Build Path”.

La forma de utilizarla es mediante un método estático:

  Picasso.with(mContext).load(getItem(position).getUrl()).into(holder.logo);
  • load” puede recibir varios parámetros, en este caso se trata de la url de la imagen a cargar
  • with“, recibe el contexto de la app
  • into“, recibe la ImageView en la que se cargará la imagen
  • También tiene otros métodos para poder setear una imagen de precarga, notificaciones en caso de error y más.

Una vez realizado todo esto solo nos falta añadir el permiso de INTERNET en el manifest para que se puedan descargar las imágenes.

<uses-permission android:name=”android.permission.INTERNET” />

Después de todo esto tendremos nuestro ListView con carga asíncrona de imágenes.

Gracias y espero que sirva de ayuda.

Fran López

Recent Posts

Curso Android – Cadena en ConstraintLayout

En este primer vídeo del curso de Android desde 0 se explica cómo maquetar una…

2 años ago

Subir JSON Firebase (Firestore Database)

Como siempre, si quieres ver directamente el vídeo donde se realiza toda la configuración pasos…

3 años ago

¿El mejor cargador inalámbrico?

En esta ocasión hacemos una review del cargador inalámbrico Samsung Wireless Charger Duo Pad de…

3 años ago

Batería Galaxy Buds Pro, ¿cuánto duran?

Primero y antes de nada, si quieres ver un vídeo detallado de esta prueba, tienes…

3 años ago

OnePlus 9 Pro, potencia, diseño y precio “ajustado”.

El 23 de marzo se presentaron los nuevos dispositivos de OnePlus. Han sido una grata…

3 años ago

Review Galaxy Buds Pro. ¿MERECEN LA PENA?

Primero y antes de nada, si quieres ver un vídeo detallado de los Samsung Galaxy…

3 años ago