min*_*rus 5 android android-4.0-ice-cream-sandwich
我们的想法是拥有一个项目列表,在单击项目后,ProgressBar将在任务完成时慢慢填充.例如,列出文件列表,每个文件都有一个"下载"按钮.单击下载按钮时,将在后台下载文件,并填写进度条,显示文件完成的接近程度.
为了实现这一点,我创建了一个AsyncTask,偶尔会在适配器上调用notifyDataSetChanged来重绘它.虽然单击一个按钮后可以正常工作,但在AsyncTask完成之前,我无法单击ListView中的其他按钮.有人可以告诉我我做错了什么吗?
我在冰淇淋三明治上的模拟器(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
}
Run Code Online (Sandbox Code Playgroud)
然后我有一个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;
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,这么好 - 这正确地呈现了列表.在我的Activity中,我有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)
}
}
Run Code Online (Sandbox Code Playgroud)
我已经尝试使用executeOnExecutor以及执行,但没有运气.DownloadTask是:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
这很有效 - 差不多.当我这样做时,单击一个按钮后,ProgressBar正常更新 - 但我不能单击ListView中的其他按钮,直到AsyncTask返回.也就是说,永远不会调用onDownloadButtonClick.如果我从onProgressUpdate函数中删除mAdapter.notifyDataSetChanged()调用,则会同时更新多个任务,但当然,列表不会失效,因此我必须滚动才能看到更改.
我做错了什么,我该如何解决这个问题?
编辑:我玩了更多,似乎调用notifyDataSetChanged的频率影响onClick是被调用还是只是丢失.使用上面的代码,通过疯狂点击我偶尔可以得到第二个下载栏开始.如果我将Thread.Sleep增加到更大的值,比如2000,那么列表就像我原先预期的那样工作.
所以新问题 - 如何在不阻止onClick被调用的情况下顺利更新ProgressBars?
编辑#2:我已将带有此问题的示例项目推送到我的GitHub帐户:https://github.com/mdkess/ProgressBarListView
我已经想到了这一点.
我没有调用notifyDataSetChanged(),而是在DownloadItem对象中存储了对每个ProgressBar的引用.然后,当滚动ListView时,当旧对象作为convertView传递时,我从旧的DownloadInfo中删除了ProgressBar并将其放在新的上.
因此,我的数组适配器的getView变为:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
final DownloadInfo info = getItem(position);
// We need to set the convertView's progressBar to null.
ViewHolder holder = null;
if(null == row) {
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.file_download_row, parent, false);
holder = new ViewHolder();
holder.textView = (TextView) row.findViewById(R.id.downloadFileName);
holder.progressBar = (ProgressBar) row.findViewById(R.id.downloadProgressBar);
holder.button = (Button)row.findViewById(R.id.downloadButton);
holder.info = info;
row.setTag(holder);
} else {
holder = (ViewHolder) row.getTag();
holder.info.setProgressBar(null);
holder.info = info;
holder.info.setProgressBar(holder.progressBar);
}
holder.textView.setText(info.getFilename());
holder.progressBar.setProgress(info.getProgress());
holder.progressBar.setMax(info.getFileSize());
info.setProgressBar(holder.progressBar);
holder.button.setEnabled(info.getDownloadState() == DownloadState.NOT_STARTED);
final Button button = holder.button;
holder.button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
info.setDownloadState(DownloadState.QUEUED);
button.setEnabled(false);
button.invalidate();
FileDownloadTask task = new FileDownloadTask(info);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
return row;
}
Run Code Online (Sandbox Code Playgroud)
然后下载AsyncTask将在progressBar上设置进度,如果它不为null,则按预期工作.
我将更正后的代码上传到GitHub,你可以在这里看到:https://github.com/mdkess/ProgressBarListView
| 归档时间: |
|
| 查看次数: |
6230 次 |
| 最近记录: |