Cargar ListView con AsyncTask en Android

Buenas tardes, en esta entrada veremos como usar una AsyncTask para cargar elementos en un ListView.


Para comenzar con el proceso, vamos a ver en que consisten las AsyncTask.

Según el sitio oficial de Android Developers, los AsyncTask nos permiten un uso propio y facil del hilo de la UI. Esta clase nos permite realizar procesos en background y publicar estos resultados en el hilo principal sin tener que manipular hilos o handlers.

Se definen las tareas asíncronas como procesos que se ejecutan en otro hilo y cuyo resultado se muestra en el hilo principal. Una tarea asíncrona se define con 3 tipos genéricos, llamados Params, Progress y Result, y de 4 pasos, onPreExecute, doInBackground, onProgressUpdate y onPostExecute.

Explicado un poco en que consisten este tipo de tareas y que contienen, vamos a usar el Ejemplo ListView Básico para editarlo y agregarle la AsyncTask.

  • Para empezar crearemos un nuevo Proyecto en Android.




    Hasta aquí no hay nada que no sepamos, creamos un proyecto con unos datos básicos, nombre de actividad, etc.

  • A continuación editamos el archivo xml de la actividad para que quede así:

    				<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    				    xmlns:tools="http://schemas.android.com/tool"
    				    android:layout_width="match_parent"
    				    android:layout_height="match_parent"
    				    tools:context=".MainActivity" >
    				
    				    <ListView 
    				        android:id="@+id/listview"
    				        android:layout_width="wrap_content"
    				        android:layout_height="wrap_content"/>
    				
    				</RelativeLayout>
    				

    Con este layout, nos debe de quedar así:

  • A continuación nos vamos al código java de nuestra clase, donde los primeros pasos con casi idénticos al tutorial anterior:

    				public class MainActivity extends Activity {
    	
    					private ListView list;
    					private String[] sistemas = {"Ubuntu", "Android", "iOS&", "Windows", "Mac OSX", 
    												"Google Chrome OS", "Debian", "Mandriva", "Solaris", "Unix"};
    				
    					@Override
    					protected void onCreate(Bundle savedInstanceState) {
    						super.onCreate(savedInstanceState);
    						setContentView(R.layout.activity_main);
    						
    						list = (ListView)findViewById(R.id.listview);
    						list.setOnItemClickListener(new OnItemClickListener(){
    				
    							@Override
    							public void onItemClick(AdapterView<?> arg0, View arg1, int posicion, long arg3) {
    								// TODO Auto-generated method stub
    								Toast.makeText(getApplicationContext(), "Ha pulsado el elemento " + posicion, Toast.LENGTH_SHORT).show();
    							}
    			
    					});
    				}
    				
  • Ahora es donde crearemos nuestra AsyncTask, dentro de la clase de nuestra actividad:

    				private class CargarListView extends AsyncTask<Void, Void, ArrayAdapter<String>>{
    					Context context;
    					ProgressDialog pDialog;
    		
    					public CargarListView(Context context){
    						this.context = context;
    					}
    		
    					@Override
    					protected void onPreExecute() {
    						// TODO Auto-generated method stub
    						super.onPreExecute();
    			
    						pDialog = new ProgressDialog(context);
    						pDialog.setMessage("Cargando Lista");
    						pDialog.setCancelable(true);
    						pDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    						pDialog.show();
    					}
    		
    					@Override
    					protected ArrayAdapter<String> doInBackground(Void... arg0) {
    						// TODO Auto-generated method stub
    			
    						try{
    							Thread.sleep(2000);
    						}catch(Exception ex){
    							ex.printStackTrace();
    						}
    				
    						ArrayAdapter<String> adaptador = new ArrayAdapter<String>(context, android.R.layout.simple_list_item_1, sistemas);
    						return adaptador;
    					}
    
    					@Override
    					protected void onPostExecute(ArrayAdapter<String> result) {
    						// TODO Auto-generated method stub
    						super.onPostExecute(result);
    						list.setAdapter(result);
    						pDialog.dismiss();
    					}
    		
    				}
    				

    Esta clase pertenece a la AsyncTask, y como tipo genérico tenemos Void (doInBackground no recibe nada), Void (no actualizamos el progreso de la tarea) y ArrayAdapter, que es el tipo de dato que retorna el método doInBackground. Tenemos como variables de instancia de la clase un ProgressDialog, el cual mostrará un diálogo con un Spinner y el mensaje de “Cargando Lista”, todo ello para que el usuario vea que nuestra aplicación no ha fallado, sino que está procesando algo.

    En el método doInBackground tenemos el proceso que se elabora en segundo plano, en este caso hemos hecho que espere 1 segundo, para que podamos ver el ProgressDialog en funcionamiento. Tras esto, se crea el ArrayAdapter y lo retorna.

    En el último método, onPostExecute, recibe el adaptador y lo aplica a la lista. En este método es donde se actualizan los elementos del hilo principal, ya que es el que se activa una vez se ha hecho todo el procesamiento. Tras establecer el adaptador en la ListView, quitamos el ProgressDialog.

Tras esto ya tendríamos nuestra AsyncTask trabajando para cargar una lista, aunque podemos usar esto de muchísimas maneras diferentes. A continuación vemos un video de como queda nuestra AsyncTask en funcionamiento:


Sin mas, espero que os sirva de algo, cualquier aporte y corrección son bienvenidos, saludos!!!

  • beto

    hola buen post, no podrias subur el codigo del proyecto, estoy haciendo algo similar con Asynctask y me gustaria poder probarlo

    • Buenas Beto, aquí tienes este proyecto para descargar 🙂

      https://dl.dropbox.com/u/64059675/EjemploAsyncTask.zip

      Espero que te sea de utilidad y te sea de ayuda !

      Saludos!

      • beto

        gracias probare y espero me funcione

      • Arturo

        Muchas gracias. Me ha sido de mucha ayuda.

      • Cuando puedas podrías subir el código.? este ya no esta disponible.
        Tengo una duda, en que momento llamas a AsyncTask desde la clase principal.?
        Yo corri la alicacion tal cual esta pero no me muestra nada.
        Saludos.!

  • beto

    no se si puedas ayudarme en mi aplicacion qe es la siguiente
    estoy utilizando un AutoCompleteTextView al cual le quiero asignar una lista de palabras (como tu lo haces al listView). para que cuando este escribiendo me aparescan las palabras qe coincidan con el texto escrito.,
    la lista que quiero asignar al AutoCompleteTextView la obtengo mediante un web service al cual le mando como parametro el texto qe estoy escribiendo para que me regrese la lista con los 10 primeros nombres que concidan.
    el problema es que lo quiero hacer mediante AsyncTask para que lalista se actualice mientras se esta escribiendo ..
    el web service ya lo tengo y si funciona
    no se si puedas ayudarme o conoscas un foro en el qe me pueda guiar sobre este tema

    • Buenos días Beto, antes de nada he estado buscando, y he encontrado un post en stackoverflow.com , en el que justo una persona tiene tu mismo caso, y al final lo soluciona:

      http://stackoverflow.com/questions/10786702/autocompletetextview-with-async-suggestions-loading-doesnt-show-dropdown

      Al parecer, lo que hace es agregar un textChangeListener, en el cual ejecuta la AsyncTask, que va obteniendo los datos y los va cargando en el ArrayList en su caso.

      Una vez ya el ArrayList ya está cargado con algo se crea el adapter, y entonces se establece. Por otro lado, una vez que ya el adapter tiene datos, y se han cargado mas datos de los que había, para “recargar” el adapter usa el método notifyDataSetChanged().

      Lo único que creo que deberías hacer es controlar cuando se crea el adapter, es decir, controlando si existe el adapter, y si no existe (sería la primera vez que recibes datos), lo creas y lo estableces, y si ya existe (cuando has obtenido mas datos), actualizarlo.

      Justo hoy tenía pendiente hacer un Tutorial básico sobre AutoCompleteTextVIew, aunque no cubría estos aspectos, pero si lo del post de stackoverflow.com te ha servido de ayuda, comunícamelo y se puede hacer una entrada que en este caso se llame “Cargar AutoCompleteTextView con AsyncTask”.

      Saludos Beto!, espero que te haya sido de ayuda!!!

      PD: Para aplicar el Listener al AutoCompleteTextView:

      txtTexto.addTextChangedListener(new TextWatcher(){
      
      			@Override
      			public void afterTextChanged(Editable arg0) {
      				// TODO Auto-generated method stub
      				
      			}
      
      			@Override
      			public void beforeTextChanged(CharSequence arg0, int arg1,
      					int arg2, int arg3) {
      				// TODO Auto-generated method stub
      				
      			}
      
      			@Override
      			public void onTextChanged(CharSequence arg0, int arg1, int arg2,
      					int arg3) {
      				// TODO Auto-generated method stub
      				
      			}
      			
      		});
      
  • beto

    gracias por la ayuda
    ingrese varios Log.i para poder revisar paso a paso lo que estaba ejecutando y poder detectar donde tenia el error:

    este error me aparece cuando intento crear el ArrayAdapter, todo va bien hasta que llego a esta
    linea:
    adaptador = new ArrayAdapter
    (context, android.R.layout.simple_expandable_list_item_2,productos);

    luego me aparece el siguiente error:
    Error Adapter<– yo le puse este nombre al error
    java.lang.NullPointerException
    at android.widget.ArrayAdapter.init(ArrayAdapter.java:271)
    at android.widget.ArrayAdapter.(ArrayAdapter.java:125)
    at com.example.wsproducto.ProdActivity$CargarProductos.doInBackground(ProdActivity.java:177)

    ya intente temb con simple_dropdown_item_1line y me da el mismo error

    • La excepción del tipo NullPointerException son aquellas que se lanzan cuando se está intentando dar un valor a una variable de referencia que es null, es decir, puede darse el caso de que el array “productos” no haya sido inicializado con la sentencia ” productos = new XXXXXX.”

      Prueba en la AsyncTask que recibe los datos del WebService, añadiendo un bucle for que recorra el array productos y que muestre lo que contiene, en el método onPostExecute().

      Saludos!!

      • Según veo ahora bien en el log, se me ha olvidado decirte, que el error creo que se está dando en el bloque de código que hay en el método doInBackground() del AsyncTask, comprueba las llamadas y a ver si no se ha escapado algo.

        Saludos y suerte con ello Beto 🙂

    • beto

      ya resolvi el problema y parece qe ya puedo llenar el AutoCompleteTextView
      pero hare algunas pruebas enviando parametros y comento si funciona

  • beto

    ya me funciona el listado mediante el web service pero ahora cuando estoy escribiendo en el AutoCompleteTextView si pongo un “espacio” me da error y se cierra la aplicacion, ademas cuando selecciono un elemento de la lista me pasa lo mismo

    he probado colocando una sola palabra y si funciona bien solo cuando coloco un espacio la aplicacion se detiene y se cierra

    • Bueno, has avanzado un paso, ahora hay que pulirlo. El tema de que se cierre con un espacio es sin duda problemático, hay que ver exactamente donde se ejecuta el error exactamente. Supongo que cuando vas creando el String para enviar al WebService vas concatenando cada letra que escribes, y cuando pulsas el espacio, algo hace que explote. Intenta poner log donde creas que se está produciendo el error, guiandote por el call stack del LogCat y a ver si con los datos que obtengamos se puede solucionar, ya sea variando un poco el algoritmo o algo.

      PD: Si por un casual tienes prisa o algo, te recomiendo usar StackOverflow, el cuál está lleno de profesionales, y aunque yo te pueda ayudar aquí y no me importe, porque así aprendemos ambos, allí son mucho mas profesionales y quizás logres solucionarlo antes.

      Saludos Berto!!! y ánimo!

      • beto

        solucionado lo que se me ocurrio hacer es remplazar los espacion por guio bajo en android y en el ewb service los _ por el espacio ya que por medio de android el web service no me permite enviar espacios no se por qe pasa esto pero asi lo solucione
        gracias por la ayuda

        • Lo importante es que funciona, aunque sigue dandole vueltas al asunto, seguramente en el momento menos pensado se te ocurra otro método jejeje.

          Saludos!

  • beto

    quisiera saber si se puede hacer lo siguiente:
    estoy cargando un listview desde un webservice y me traigo los 10 primeros resultados que conciden con los parametros que envio, pero lo que quiero hacer es que cuando le le scroll al list view y este me muestre el ultimo resultado hacer algo similar a facebook que me empieza a cargar mas resultados, ess decir qe me cargue otros 10 resultadosy me estaria mostrando ahora 20 resultados y asi sucesivamente.
    yo estaria enviando todos los parametros para esto solo que no se si el listview tenga algun evento o pueda hacer esto ya que e buscado y no lo e encontrado

  • Pingback: AsyncTask en Service en Android « SekthDroid()

  • Hola, estoy haciendo una app que carga los contactos y los compara en un servidor para saber si esta registrado el numero, la cosa es que tarda mucho y lo tengo así como en tu ejemplo, es decir, hace el proceso y después muestra la lista. Me gustaría saber sí es posible que vaya arrojando cada elemento una vez terminado el proceso para ese contacto, para que el usuario no se desespere porque entre mas contactos tenga mas tarda en cargar.