La clase ListView es un componente muy usado en aplicaciones Android para mostrar listas de datos con cierto formato. Hoy en dia los móviles de gama media-alta pueden hacer funcionar código sin optimizar a una velocidad tolerable. Sin embargo, un gran porcentaje de dispositivos Android aun utilizan procesadores de un solo núcleo y con una memoria bastante limitada. Este post explica una manera sencilla para el desarrollador de optimizar este componente.
Para crear nuestro listview personalizado necesitamos un adapter personalizado tambien. Un Adapter gestiona los datos de la lista y controla como se van mostrando.
Nuestro primer paso es crear la plantilla de la fila de la lista con 3 elementos, un icono, un nombre y un valor numérico, lo llamaremos listview_row.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="?android:attr/listPreferredItemHeight" android:padding="6dip"> <ImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_alignParentBottom="true" android:layout_alignParentTop="true" android:layout_marginRight="6dip" android:contentDescription="iconrow" android:src="@drawable/ic_launcher" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_alignParentBottom="true" android:layout_alignParentTop="true" android:gravity="center_vertical" android:layout_toRightOf="@id/icon"> <TextView android:id="@+id/firstLine" android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="center_vertical" android:text="Item name" android:layout_weight="1" android:textSize="16sp" /> <TextView android:id="@+id/secondLine" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="marquee" android:singleLine="true" android:text="Amount" android:gravity="end" android:textSize="12sp" /> </LinearLayout> </RelativeLayout>
Crearemos una clase muy sencilla para enlazar con los datos de la lista y que nuestro adapter pueda usarla
public class MyListViewRow { private long _id; private String _name; private int _amount; public MyListViewRow(long id, String name,int amount) { _id = id; _name = name; _amount = amount; } public long get_id(){ return _id;} public String get_name(){return _name;} public int get_amount(){return _amount;} }
Ahora añadimos el código de nuestro Adapter. Usamos el patrón de diseño ViewHolder para evitar realizar llamadas con demasiada frecuencia a findViewById() durante el desplazamiento por el ListView, mejorando así su rendimiento
public class MyListViewAdapter extends BaseAdapter { static class ViewHolder { ImageView iconIV; TextView nameTV; TextView amountTV; } private LayoutInflater _inflater; private Context _context; private ArrayList _values; public MyListViewAdapter(Context context, ArrayList values) { _context = context; _values = values; _inflater = LayoutInflater.from(_context); } @Override public int getCount() { return _values.size(); } @Override public Object getItem(int position) { return _values.get(position); } @Override public long getItemId(int position) { return _values.get(position).get_id(); } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = _inflater.inflate(R.layout.listview_row, null); holder = new ViewHolder(); holder.nameTV = (TextView) convertView.findViewById(R.id.firstLine); holder.amountTV = (TextView) convertView.findViewById(R.id.secondLine); holder.iconIV = (ImageView) convertView.findViewById(R.id.icon); convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } holder.nameTV.setText(_values.get(position).get_name()); holder.amountTV.setText(Integer.toString(_values.get(position).get_amount())); return convertView; } }
La primera vez que un elemento de la lista es cargado convertView es null. Necesitamos hacer un inflate de nuestra plantilla del elemento de la lista, instanciar el viewholder y encontrar el componente visual de la interfaz usando findViewById() para asignarlo al viewholder, y añadirlo como tag del convertView.
Las próximas veces que se cargue, convertView no es nulo y no necesitaremos realizar el inflate de la plantilla y, lo más importante para el rendimiento, no tenemos que volver a realizar otra llamada a findViewById(), podemos acceder a los componentes del elemento de la lista usando el viewholder del convertView.
Ahora podemos modificar la plantilla principal añadiendo un componente listview.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <ListView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/mainLV" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true"/> </RelativeLayout>
Y añadimos el siguiente código en la clase principal para usar nuestro listview
private ListView _mainListView; private MyListViewAdapter _mainListViewAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Input list data ArrayList _inputdata = new ArrayList(); MyListViewRow rowData; for (int i = 0; i < 20; ++i) { rowData = new MyListViewRow(i*3,"Element "+ Integer.toString(i), i*2); _inputdata.add(rowData); } //find list view _mainListView = (ListView) findViewById(R.id.mainLV); //create an adapter to listview and assign it _mainListViewAdapter = new MyListViewAdapter(_mainListView.getContext(), _inputdata); _mainListView.setAdapter(_mainListViewAdapter); //click listener example _mainListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { MyListViewRow clickRowData = (MyListViewRow) _mainListView.getItemAtPosition(position); Toast.makeText(getApplicationContext(), Long.toString(clickRowData.get_id()) + " " + clickRowData.get_name() + " " + Integer.toString(clickRowData.get_amount()) , Toast.LENGTH_LONG).show(); } }); }
Finalmente se mostrará nuestra lista optimizada
Tutorial files
Te puede interesar:
Ayudanos con este blog!
El último año he estado dedicando cada vez más tiempo a la creación de tutoriales, en su mayoria sobre desarrollo de videojuegos. Si crees que estos posts te han ayudado de alguna manera o incluso inspirado, por favor considera ayudarnos a mantener este blog con alguna de estas opciones. Gracias por hacerlo posible!