アイデアは、アイテムのリストを作成することです。アイテムをクリックした後、タスクが完了するとProgressBarがゆっくりといっぱいになります。たとえば、ファイルのリストを作成し、各ファイルに[ダウンロード]ボタンを付けます。ダウンロードボタンをクリックすると、ファイルがバックグラウンドでダウンロードされ、進行状況バーが表示され、ファイルが完了にどれだけ近づいているかが示されます。
これを実現するために、アダプターでnotifyDataSetChangedを呼び出して再描画するAsyncTaskを作成します。これは1つのボタンがクリックされた後は機能しますが、AsyncTaskが完了するまで、ListViewの他のボタンをクリックすることはできません。誰かが私が間違っていることを教えてもらえますか?
私はこれをIceCreamSandwichのエミュレーター(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
}
次に、DownloadItemのリストをListViewに適合させるArrayAdapterがあります。
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でこれを試しただけでなく、実行するだけでしたが、運がありませんでした。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();
}
}
これは機能します-ほとんど。これを行うと、1つのボタンをクリックした後、ProgressBarは通常どおり更新されますが、AsyncTaskが戻るまで、ListViewの他のボタンをクリックすることはできません。つまり、onDownloadButtonClickが呼び出されることはありません。onProgressUpdate関数からmAdapter.notifyDataSetChanged()呼び出しを削除すると、複数のタスクが同時に更新されますが、もちろんリストは無効化されないため、スクロールして変更を確認する必要があります。
私は何が間違っているのですか、そしてどうすればこれを修正できますか?
編集:これをもう少し試してみましたが、notifyDataSetChangedを呼び出す頻度は、onClickが呼び出されているか、単に失われているかに影響するようです。上記のコードでは、必死にクリックすることで、2番目のダウンロードバーを起動できる場合があります。Thread.Sleepを2000のようにもっと大きなものに増やすと、リストは当初の期待どおりに機能します。
だから新しい質問-onClickの呼び出しをブロックせずにProgressBarsをスムーズに更新するにはどうすればよいですか?
編集#2:この問題のあるサンプルプロジェクトをGitHubアカウントにプッシュしました:https ://github.com/mdkess/ProgressBarListView