{"id":1634,"date":"2018-10-17T13:46:51","date_gmt":"2018-10-17T18:46:51","guid":{"rendered":"http:\/\/naps.com.mx\/blog\/?p=1634"},"modified":"2018-10-17T16:42:34","modified_gmt":"2018-10-17T21:42:34","slug":"optimizar-un-listview-usando-viewholder","status":"publish","type":"post","link":"https:\/\/naps.com.mx\/blog\/optimizar-un-listview-usando-viewholder\/","title":{"rendered":"Optimizar un ListView usando ViewHolder"},"content":{"rendered":"<p><strong>Veremos c\u00f3mo optimizar un <em>ListView<\/em> usando <em>ViewHolder<\/em> para trabajar con listas grandes sin perder rendimiento en nuestra aplicaci\u00f3n.<\/strong><\/p>\n<p><!--more--><\/p>\n<p>En el art\u00edculo anterior vimos <a href=\"http:\/\/naps.com.mx\/blog\/uso-de-un-listview-en-android\/\">c\u00f3mo usar un <em><strong>ListView<\/strong><\/em> en Android para mostrar una lista de datos<\/a>.<span class=\"Apple-converted-space\">\u00a0 <\/span>Ahora bien, \u00bfqu\u00e9 pasar\u00eda si esa lista de datos fuera muy grande? Por ejemplo, en una aplicaci\u00f3n que contenga contactos, \u00e9sta lista f\u00e1cilmente puede alcanzar los 100 elementos o m\u00e1s a\u00fan.<span class=\"Apple-converted-space\">\u00a0<\/span><\/p>\n<p>Veamos el c\u00f3digo propuesto para mostrar un ListView:<\/p>\n<pre class=\"lang:java decode:true \">import android.content.Context;\r\nimport android.view.LayoutInflater;\r\nimport android.view.View;\r\nimport android.view.ViewGroup;\r\nimport android.widget.BaseAdapter;\r\nimport android.widget.TextView;\r\nimport java.util.ArrayList;\r\n\r\npublic class MyAdapter extends BaseAdapter {\r\n\u00a0 \u00a0 private Context context;\r\n\u00a0 \u00a0 private int layout;\r\n\u00a0 \u00a0 private ArrayList&lt;String&gt; names;\r\n\u00a0 \u00a0 public MyAdapter(Context context, int layout, ArrayList&lt;String&gt; names){\r\n\u00a0 \u00a0 \u00a0 \u00a0 this.context = context;\r\n\u00a0 \u00a0 \u00a0 \u00a0 this.layout = layout;\r\n\u00a0 \u00a0 \u00a0 \u00a0 this.names = names;\r\n\u00a0 \u00a0 }\r\n\r\n\u00a0 \u00a0 @Override\r\n\u00a0 \u00a0 public int getCount() {\r\n\u00a0 \u00a0 \u00a0 \u00a0 return this.names.size();\r\n\u00a0 \u00a0 }\r\n\r\n\u00a0 \u00a0 @Override\r\n\u00a0 \u00a0 public Object getItem(int position) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 return this.names.get(position);\r\n\u00a0 \u00a0 }\r\n\r\n\u00a0 \u00a0 @Override\r\n\u00a0 \u00a0 public long getItemId(int id) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 return id;\r\n\u00a0 \u00a0 }\r\n\r\n\u00a0 \u00a0 @Override\r\n\r\n\u00a0 \u00a0 public View getView(int position, View convertView, ViewGroup viewGroup) {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ Copiamos la vista\r\n\u00a0 \u00a0 \u00a0 \u00a0 View v = convertView;\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/Inflamos la vista con nuestro propio layout\r\n\u00a0 \u00a0 \u00a0 \u00a0 LayoutInflater layoutInflater = LayoutInflater.from(this.context);\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 v= layoutInflater.inflate(R.layout.list_item, null);\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ Valor actual seg\u00fan la posici\u00f3n\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 String currentName\u00a0 = names.get(position);\r\n\u00a0 \u00a0 \u00a0 \u00a0\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/ Referenciamos el elemento a modificar y lo rellenamos\r\n\u00a0 \u00a0 \u00a0 \u00a0 TextView textView = (TextView) v.findViewById(R.id.textView);\r\n\u00a0 \u00a0 \u00a0 \u00a0 textView.setText(currentName);\r\n\u00a0 \u00a0 \u00a0 \u00a0 \/\/Devolvemos la vista inflada\r\n\u00a0 \u00a0 \u00a0 \u00a0 return v;\r\n\u00a0 \u00a0 }<\/pre>\n<p>Pues bien, \u00e9ste c\u00f3digo hace que, cada vez que el usuario hace un desplazamiento hacia arriba o hacia abajo en la lista, se ejecuta \u00e9sta l\u00ednea:<\/p>\n<pre class=\"lang:java decode:true\">TextView textView = (TextView) v.findViewById(R.id.textView);<\/pre>\n<p>Como puedes notar, contiene una funci\u00f3n <strong>findViewById<\/strong>, que es una funci\u00f3n bastante pesada dentro de Android.<\/p>\n<p>Una forma de optimizar la aplicaci\u00f3n es haciendo los siguiente:<\/p>\n<ol>\n<li>Si es la primera vez que el elemento va a ser mostrado en pantalla, mandar a llamar a findViewById.<\/li>\n<li>Si el elemento ya ha sido mostrado en pantalla, no mandar a llamar a findViewById, sino mostrar el dato ya guardado.<\/li>\n<\/ol>\n<p>Para realizar esto, utilizamos el patr\u00f3n <strong><em>ViewHolder<\/em><\/strong>, que mejorar\u00e1 el rendimiento de la aplicaci\u00f3n y en consecuencia la experiencia de uso.<span class=\"Apple-converted-space\">\u00a0<\/span><\/p>\n<h2>ListView usando ViewHolder<\/h2>\n<p>Realizaremos los cambios necesarios al c\u00f3digo de modo que realice las siguientes instrucciones:<\/p>\n<pre class=\"lang:java decode:true\">import android.content.Context;\r\nimport android.view.LayoutInflater;\r\nimport android.view.View;\r\nimport android.view.ViewGroup;\r\nimport android.widget.BaseAdapter;\r\nimport android.widget.TextView;\r\nimport java.util.ArrayList;\r\n\r\npublic class MyAdapter extends BaseAdapter {\r\n\r\n    private Context context;\r\n    private int layout;\r\n    private ArrayList&lt;String&gt; names;\r\n\r\n    public MyAdapter(Context context, int layout, ArrayList&lt;String&gt; names){\r\n        this.context = context;\r\n        this.layout = layout;\r\n        this.names = names;\r\n    }\r\n\r\n    @Override\r\n    public int getCount() {\r\n        return this.names.size();\r\n    }\r\n\r\n    @Override\r\n    public Object getItem(int position) {\r\n        return this.names.get(position);\r\n    }\r\n\r\n    @Override\r\n    public long getItemId(int id) {\r\n        return id;\r\n    }\r\n\r\n    @Override\r\n    public View getView(int position, View convertView, ViewGroup viewGroup) {\r\n        ViewHolder holder;\r\n        if (convertView == null){\r\n            \/\/Inflamos la vista con nuestro propio layout\r\n            LayoutInflater layoutInflater = LayoutInflater.from(this.context);\r\n            convertView = layoutInflater.inflate(R.layout.list_item, null);\r\n            holder = new ViewHolder();\r\n            \/\/ Referenciamos el elemento a modificar y lo rellenamos\r\n            holder.nameTextView = (TextView) convertView.findViewById(R.id.textView);\r\n            convertView.setTag(holder);\r\n        }\r\n        else{\r\n            holder = (ViewHolder) convertView.getTag();\r\n        }\r\n\r\n        \/\/ Valor actual seg\u00fan la posici\u00f3n\r\n        String currentName  = names.get(position);\r\n        holder.nameTextView.setText(currentName);\r\n\r\n        \/\/Devolvemos la vista inflada\r\n        return convertView;\r\n    }\r\n    \r\n    static class ViewHolder{\r\n        private TextView nameTextView; \/\/ Solo tenemos un textview\r\n    }\r\n}\r\n<\/pre>\n<p>Notamos que:<\/p>\n<p>Se determina si es la primera vez que se cargan los datos al analizar si <em><strong>convertView<\/strong><\/em> (la vista) viene nulo.<\/p>\n<pre class=\"lang:java decode:true \">if (convertView == null)<\/pre>\n<p>Si es as\u00ed, realizamos el procedimiento normal, que incluye la llamada (o llamadas, si son varios elementos) hacia <strong>findViewById<\/strong>.<\/p>\n<p>Se guarda el dato dentro del atributo <em><strong>nameTextView<\/strong><\/em> que pertenece a la clase <em><strong>ViewHolder<\/strong><\/em>.<\/p>\n<pre class=\"lang:java decode:true\">holder.nameTextView = (TextView) convertView.findViewById(R.id.textView);<\/pre>\n<p>Cuando el <em><strong>convertView<\/strong><\/em> no es nulo, quiere decir que el elemento ya hab\u00eda sido renderizado (ya fue mostrado en pantalla anteriormente), por lo que no se manda a llamar a la funci\u00f3n <strong>findViewById<\/strong>, sino que se recupera del dato guardado en el holder (o portador).<\/p>\n<div id=\"attachment_1637\" style=\"width: 468px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/10\/09-ListView-usando-ViewHolder.jpeg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-1637\" class=\"size-full wp-image-1637\" src=\"http:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/10\/09-ListView-usando-ViewHolder.jpeg\" alt=\"ListView usando ViewHolder\" width=\"458\" height=\"796\" srcset=\"https:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/10\/09-ListView-usando-ViewHolder.jpeg 458w, https:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/10\/09-ListView-usando-ViewHolder-173x300.jpeg 173w\" sizes=\"auto, (max-width: 458px) 100vw, 458px\" \/><\/a><p id=\"caption-attachment-1637\" class=\"wp-caption-text\">ListView usando ViewHolder<\/p><\/div>\n<h3>Referencias<\/h3>\n<ol>\n<li><strong>De la Torre (2015). <a href=\"http:\/\/enekodelatorre.com\/el-patron-viewholder\/\" class=\"broken_link\">El patr\u00f3n ViewHolder, qu\u00e9 es y c\u00f3mo utilizarlo<\/a>.<\/strong> Disponible en [http:\/\/enekodelatorre.com\/el-patron-viewholder\/] consultado el [17-octubre-2018].<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Veremos c\u00f3mo optimizar un ListView usando ViewHolder para trabajar con listas grandes sin perder rendimiento en nuestra aplicaci\u00f3n.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"amp_status":"","footnotes":""},"categories":[234],"tags":[198,235,238],"class_list":["post-1634","post","type-post","status-publish","format-standard","hentry","category-aplicaciones-moviles","tag-android","tag-desarrollo-de-aplicaciones-moviles","tag-listview"],"_links":{"self":[{"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/posts\/1634","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/comments?post=1634"}],"version-history":[{"count":4,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/posts\/1634\/revisions"}],"predecessor-version":[{"id":1645,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/posts\/1634\/revisions\/1645"}],"wp:attachment":[{"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/media?parent=1634"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/categories?post=1634"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/tags?post=1634"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}