这个想法是有一个项目列表,在单击项目后,进度条将随着任务完成而慢慢填充。例如,描绘一个文件列表,每个文件都有一个下载按钮。单击下载按钮时,文件会在后台下载,并且会填充一个进度条,显示文件距离完成有多近。
为此,我创建了一个 AsyncTask,它偶尔会在适配器上调用 notifyDataSetChanged 来重绘它。虽然这在单击一个按钮后有效,但在 AsyncTask 完成之前,我无法单击 ListView 中的其他按钮。有人可以告诉我我做错了什么吗?
我在 Ice Cream Sandwich 的模拟器 (x86) 上运行它。
我有一个 DownloadItem 来表示下载的进度(为简洁起见,下面的代码被简化了):
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
}
然后我有一个 ArrayAdapter,它为 ListView 调整了 DownloadItem 列表:
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;
}
}
到目前为止,一切都很好 - 这可以正确呈现列表。在我的活动中,我有 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)
}
}
我已经用 executeOnExecutor 尝试过这个,也只是执行,但没有运气。下载任务是:
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();
}
}
这有效 - 几乎。当我这样做时,单击一个按钮后,ProgressBar 会正常更新 - 但在 AsyncTask 返回之前,我无法单击 ListView 中的其他按钮。也就是说,永远不会调用 onDownloadButtonClick。如果我从 onProgressUpdate 函数中删除 mAdapter.notifyDataSetChanged() 调用,多个任务会同时更新,但当然,列表不会失效,因此我必须滚动查看更改。
我做错了什么,我该如何解决这个问题?
编辑:我又玩了一些,似乎调用 notifyDataSetChanged 的频率会影响 onClick 是被调用还是丢失。使用上面的代码,通过疯狂的点击,我偶尔可以得到第二个下载栏来启动。如果我将 Thread.Sleep 增加到更大的值,例如 2000,则该列表将按我最初的预期工作。
所以新问题 - 我如何让 ProgressBars 顺利更新而不阻止 onClick 被调用?
编辑#2:我已将一个有此问题的示例项目推送到我的 GitHub 帐户:https ://github.com/mdkess/ProgressBarListView