The idea is to have a list of items where after clicking an item, a ProgressBar will slowly fill as the task is completed. For example, picture a list of files, with a Download button by each one. When the download button is clicked, the file is downloaded in the background and a progress bar is filled showing how close the file is to completion.
To accomplish this, I create an AsyncTask which occasionally calls notifyDataSetChanged on the adaptor to redraw it. While this works after one button is clicked, until the AsyncTask is completed, I am unable to click other buttons in the ListView. Can someone please tell me what I am doing wrong?
I am running this on the emulator (x86) on Ice Cream Sandwich.
I have a DownloadItem to represent the progress of a download (the code below is simplified for brevity):
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
}
I then have an ArrayAdapter that adapts a list of DownloadItem for the 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;
}
}
So far, so good - this properly renders the list. In my Activity, I have the 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)
}
}
I've tried this with executeOnExecutor as well as just execute, but with no luck. DownloadTask is:
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();
}
}
This works - almost. When I do this, after clicking one button, the ProgressBar updates as normal - but I can't click other buttons in the ListView until the AsyncTask returns. That is, onDownloadButtonClick is never called. If I remove the mAdapter.notifyDataSetChanged() call from the onProgressUpdate function, multiple tasks are updated simultaneously, but of course, the list isn't invalidated and so I have to scroll around to see changes.
What am I doing wrong, and how can I fix this?
Edit: I played with this some more, and it seems that the frequency of calling notifyDataSetChanged affects whether onClick is being called or just being lost. With the above code, by clicking frantically I can occasionally get a second download bar to start. If I increase the Thread.Sleep to something much larger, like 2000, the list works as I originally expected.
So new question - how do I get the ProgressBars to update smoothly while not blocking onClick from being called?
EDIT #2: I have pushed a sample project with this problem to my GitHub account: https://github.com/mdkess/ProgressBarListView