2012-06-26 13 views
5

アイデアはアイテムをクリックした後、ProgressBarがタスクが完了すると徐々に塗りつぶされるアイテムのリストを持つことです。たとえば、ファイルの一覧をそれぞれのダウンロードボタンで表示します。ダウンロードボタンをクリックすると、ファイルがバックグラウンドでダウンロードされ、ファイルがどれくらい近くに完了するかを示すプログレスバーが表示されます。ListView/OnClickにProgressBarを追加すると、一度だけ呼び出される

これを達成するために、アダプタでnotifyDataSetChangedを呼び出すことがあるAsyncTaskを作成して、再描画します。 AsyncTaskが完了するまで、このボタンがクリックされた後で動作しますが、ListViewの他のボタンをクリックすることはできません。誰かが私が間違っていることを教えてもらえますか?

アイスクリームサンドイッチのエミュレータ(x86)で実行しています。

:私は、リストビューのためDownloadItemのリストを適応させるArrayAdapterを有する

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は、ダウンロードの進行状況(以下のコードを簡潔にするために簡略化されている)を表すために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; 
    } 
} 

これまでのところ、これはうまくいきました。これはリストを適切にレンダリングします。私もちょうど実行などexecuteOnExecutorでこれを試してみましたが、運とき

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) 
    } 
} 

:私の活動では、私がonClickListenerを持っています。ダウンロードタスクは:

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をスムーズに更新するにはどうすればいいですか?

EDIT#2:私は私のGitHubのアカウントにこの問題でのサンプルプロジェクトをプッシュしている:あなたは、次のようにアダプタ自体でアイテムをダウンロードするには、クリックリスナーを設定https://github.com/mdkess/ProgressBarListView

+1

をあなたがリストビューを使用しているようですか?もしそうなら、時間があれば、これを見る価値があると思う:[Google I/O 2010 - リストビュー](http://www.youtube.com/watch?v=wDBM6wVEO70)。あなたは 'Thread.sleep(800)'(例えば)を使うことができます。 '10'は必要ありません。 –

+1

クリックリスナーはどこで設定しますか?あなたはダミーコードでこれを見逃します。 – reTs

+0

@reTS:ボタンのアンドロイド:onClickパラメータを使用してXMLに設定しました。ハンドラは間違いなく呼び出されています。私の編集を質問にも見てください。 – mindvirus

答えて

5

私はこれを考え出した。

notifyDataSetChanged()を呼び出す代わりに、各ProgressBarへの参照をDownloadItemオブジェクトに格納しました。その後、ListViewをスクロールすると、古いオブジェクトがconvertViewとして渡されたときに、私は古いDownloadInfoからProgressBarを削除し、新しいものに置きました。それがnullではなかった場合

@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は、その後、プログレスバーの進捗状況を設定します、そして予想通り、これは働いていた:

このように、私のアレイアダプタのgetViewメソッドはその後になりました。私はGitHubのに修正されたコードをアップロード

、あなたはここでそれを見ることができます:https://github.com/mdkess/ProgressBarListView

+0

+1素晴らしいパフォーマンス。乾杯!!!! – skygeek

+0

こんにちは....あなたのコードは私にとって大きな助けになっていました...しかし、私は問題があります。アクティビティでコードを実行すると、あまりにもうまく機能します。フラグメントでコードを実行すると、プログレスバーオブジェクトnullが返されます。それはなぜそうですか? –

+0

1つまたは2つのアイテムが進行中の場合にアイテムを削除するとどうなりますか?私が試しているので、アイテムを削除すると、progressBarsのバグ。 – Cocorico

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; 
} 
+0

悲しいことに、それはうまくいっていませんでした - それはonClickにさえ達していません。私はそれがnotifyDataSetChangedをあまりにも頻繁に呼び出すことと関係していると思うので、私は今実際の進行状況バーへの参照を保持して実験しています。しかし、良い考え。 (ところで、私はあなたにdownvoteしなかった)。 – mindvirus

+0

ありがとうございます。誰が私に投票したかは気にしないでください。実際には、あなたの質問を正しく読まなかった。私は、行ビューにタグを設定すると思った。しかし、ボタンをダウンロードするタグを設定した。だから、私はあなたにこの解決策を教えた。はい、進行状況バーの参照を渡し、進行状況バーを直接更新する必要があります..あなたは非常に良い方法と私もあなたの実装が好きです:-) .. – Veer

関連する問題