BaseAdapter que hace que ListView se descomponga cuando se desplaza

Tengo problemas con algún código BaseAdapter que adapté de un libro. He estado usando variaciones de este código por todas partes en mi aplicación, pero recién me di cuenta de que al desplazarme por una larga lista, los elementos en el ListView se mezclan y no se muestran todos los elementos.

Es muy difícil describir el comportamiento exacto, pero es fácil ver si toma una lista ordenada de 50 elementos y comienza a desplazarse hacia arriba y hacia abajo.

class ContactAdapter extends BaseAdapter { ArrayList mContacts; public ContactAdapter(ArrayList contacts) { mContacts = contacts; } @Override public int getCount() { return mContacts.size(); } @Override public Object getItem(int position) { return mContacts.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view; if(convertView == null){ LayoutInflater li = getLayoutInflater(); view = li.inflate(R.layout.groups_item, null); TextView label = (TextView)view.findViewById(R.id.groups_item_title); label.setText(mContacts.get(position).getName()); label = (TextView)view.findViewById(R.id.groups_item_subtitle); label.setText(mContacts.get(position).getNumber()); } else { view = convertView; } return view; } } 

Solo está colocando datos en los widgets TextView cuando se crean por primera vez. Debes mover estas cuatro líneas:

  TextView label = (TextView)view.findViewById(R.id.groups_item_title); label.setText(mContacts.get(position).getName()); label = (TextView)view.findViewById(R.id.groups_item_subtitle); label.setText(mContacts.get(position).getNumber()); 

para estar después del bloque if / else y antes de que el método regrese, por lo que actualiza los widgets TextView si está reciclando la fila o creando una nueva.

Para aclarar aún más la respuesta de CommonsWare, aquí hay algo más de información:

La operación li.inflate (necesaria aquí para analizar el diseño de una fila desde XML y crear el objeto View apropiado) está envuelta por una statement if (convertView == null) para la eficiencia, por lo que no ocurrirá la inflación del mismo objeto una y otra vez cada vez que aparece a la vista.

SIN EMBARGO, las otras partes del método getView se utilizan para establecer otros parámetros y, por lo tanto, NO deberían incluirse en la statement if (convertView == null) {} … else {} .

En muchas implementaciones comunes de este método, algunos elementos de la etiqueta textView, ImageView o ImageButton deben ser poblados por valores de la lista [posición], usando findViewById y después de eso operaciones .setText o .setImageBitmap . Estas operaciones deben venir después de crear una vista desde cero por inflación y obtener una vista existente si no es nula (por ejemplo, en una actualización).

Otro buen ejemplo en el que se aplica esta solución para un ListView ArrayAdapter aparece en https://stackoverflow.com/a/3874639/978329