6

アイデアは、アイテムのリストを作成することです。アイテムをクリックした後、タスクが完了すると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

4

2 に答える 2

6

私はこれを理解しました。

notifyDataSetChanged() を呼び出す代わりに、各 ProgressBar への参照を DownloadItem オブジェクトに格納しました。次に、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;
  }

ダウンロードの AsyncTask は、progressBar が null でない場合は進行状況を設定し、これは期待どおりに機能しました。

修正したコードを GitHub にアップロードしました。ここで確認できます: https://github.com/mdkess/ProgressBarListView

于 2012-07-01T20:52:11.287 に答える
1

次のように、アダプター自体でアイテムをダウンロードするようにクリックリスナーを設定しようとしましたか:

@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");
    }

    final 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);

    downloadButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            new DownloadTask(DownloadArrayAdapter.this, item).execute();
        }
    });

    return row;
}
于 2012-06-26T04:22:59.543 に答える