La idea es tener una lista de elementos donde, después de hacer clic en un elemento, una barra de progreso se llene lentamente a medida que se completa la tarea. Por ejemplo, imagine una lista de archivos, con un botón Descargar por cada uno. Cuando se hace clic en el botón de descarga, el archivo se descarga en segundo plano y se llena una barra de progreso que muestra qué tan cerca está el archivo.Agregar una barra de progreso a un ListView/OnClick llamado una sola vez
Para lograr esto, creo una AsyncTask que ocasionalmente llama a notifyDataSetChanged en el adaptador para volver a dibujarlo. Si bien esto funciona después de hacer clic en un botón, hasta que se complete la tarea AsyncTask, no puedo hacer clic en otros botones en ListView. ¿Puede alguien decirme qué estoy haciendo mal?
Estoy ejecutando esto en el emulador (x86) en Ice Cream Sandwich.
tengo una de Descargar para representar el progreso de una descarga (el código de abajo se simplifica por razones de brevedad):
class DownloadItem {
public String name; // Name of the file being downloaded
public Integer progress; // How much is downloaded so far
public Integer length; // Size of the file
}
entonces tengo una ArrayAdapter que se adapta una lista de de Descargar para el ListView:
class DownloadArrayAdapter extends ArrayAdapter<DownloadItem> {
List<DownloadItem> mItems;
public DownloadArrayAdapter(List<DownloadItem> items) {
mItems = items;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if(row == null) {
// Inflate
Log.d(TAG, "Starting XML inflation");
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.download_list_item, parent, false);
Log.d(TAG, "Finished XML inflation");
}
DownloadItem item = mItems.get(position);
ProgressBar downloadProgressBar = (ProgressBar) row.findViewById(R.id.downloadProgressBar);
Button downloadButton = (Button) row.findViewById(R.id.downloadButton);
downloadButton.setTag(item);
downloadProgressBar.setMax(item.length);
downloadProgressBar.setProgress(item.progress);
return row;
}
}
Hasta ahora, muy bien, esto representa la lista. En mi actividad, tengo el OnClickListener:
class DownloadActivity extends Activity {
//...
public void onDownloadButtonClick(View view) {
DownloadItem item = (DownloadInfo)view.getTag();
DownloadArrayAdapter adapter = (DownloadArrayAdapter) view.getAdapter();
new DownloadTask(adapter, item).execute();
//new DownloadTask(adapter, item).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
}
He intentado esto con executeOnExecutor, así como sólo hay que ejecutar, pero sin suerte. DownloadTask es:
class DownloadTask extends AsyncTask<Void, Integer, Void> {
ArrayAdapter<?> mAdapter;
DownloadItem mItem;
public DownloadTask(ArrayAdapter<?> adapter, DownloadItem item) {
mItem = item;
}
//Dummy implementation
@Override
public Void doInBackground(Void ... params) {
for(int i=0; i<mItem.length; ++i) {
Thread.sleep(10); publishProgress(i);
}
return null;
}
@Override
public void onProgressUpdate(Integer ... values) {
mItem.progress = values[0];
mAdapter.notifyDataSetChanged();
}
}
Esto funciona - casi. Cuando hago esto, después de hacer clic en un botón, ProgressBar se actualiza normalmente, pero no puedo hacer clic en otros botones en ListView hasta que devuelva AsyncTask. Es decir, onDownloadButtonClick nunca se llama. Si elimino la llamada mAdapter.notifyDataSetChanged() de la función onProgressUpdate, se actualizan varias tareas simultáneamente, pero, por supuesto, la lista no se invalida, por lo que tengo que desplazarme para ver los cambios.
¿Qué estoy haciendo mal y cómo puedo solucionarlo?
Editar: Jugué con esto un poco más, y parece que la frecuencia de las llamadas notifyDataSetChanged afecta si se está llamando a onClick o simplemente se está perdiendo. Con el código anterior, al hacer clic frenéticamente, ocasionalmente puedo obtener una segunda barra de descarga para comenzar. Si aumente el Thread.Sleep a algo mucho más grande, como 2000, la lista funciona como esperaba originalmente.
Nueva pregunta: ¿cómo obtengo que ProgressBars se actualice sin problemas mientras no se bloquea el uso de OnClick?
editar # 2: He empujado a un proyecto de ejemplo de este problema a mi cuenta de GitHub: https://github.com/mdkess/ProgressBarListView
Parece que utilice la vista de lista? Si es así, y si tiene tiempo, creo que vale la pena ver esto: [Google I/O 2010 - Vista de lista] (http://www.youtube.com/watch?v=wDBM6wVEO70). Una sugerencia: puede usar 'Thread.sleep (800)' (por ejemplo), '10' no es necesario. –
¿Dónde configura el oyente de clic? Extrañas esto en el código ficticio. – reTs
@reTS: lo configuré en XML, con el parámetro android: onClick para el botón. Definitivamente se llama al controlador, una vez, de todos modos. Ver mi edición a la pregunta también. – mindvirus