ListView の Row データに ProgressBar を表示したい

1,335 views
Skip to first unread message

ma

unread,
Nov 4, 2009, 10:49:46 AM11/4/09
to Android-SDK-Japan
スレッド処理の進捗状況を、ListView 内の Row データに表示したいのですが、うまくいきません・・・

ListView に追加する Row データのレイアウトは以下のように定義しています。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/ITEM_TITLE"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>

<ProgressBar
android:id="@+id/ITEM_PROGRESS"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal">
</ProgressBar>

</LinearLayout>

BaseAdapter の拡張クラスを独自に作成し、以下のようにスレッド内で、ProgressBar の値を更新していますが、表示上の進捗率が
全く更新されません。

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

listview = (ListView) findViewById(R.id.LIST);

adapter = new ExtendAdapter(this); // BaseAdapter 拡張クラス (ListView に追加
する Row データとマッピング)

listview.setAdapter(adapter);

progress = new ProgressBar(this);
String title = "hoge";

adapter.addData(videoTitle, progress);

}

public void run() {

while((size = in.read(buf)) != -1) {
currentReadBytes += buf.length;

// ProgressBar に進捗率を送信
dlProgress = (int) ( (currentReadBytes / totalBytes) * 100);
Message msg = handler.obtainMessage();
Bundle b = new Bundle();
b.putInt("progressValue", dlProgress);
msg.setData(b);
handler.sendMessage(msg);
}

}

private Handler handler = new Handler() {
public void handleMessage(Message msg) {

int total = msg.getData().getInt
("progressValue");
// ProgressBar 進捗率更新
progress.setProgress(total);
};

progress.setProgress(total) の引数 total の値は正常に設定されているようですが、表示上進捗バーが更新されず、
解決策が一向に見つかりません。
どなたか、ご教授いただけますでしょうか。

egg

unread,
Nov 4, 2009, 7:33:58 PM11/4/09
to android-...@googlegroups.com
maさん

江川です。
新たなスレッドを開始する部分のソースが含まれていなかったので、
正確なことはわからないのですが、ちゃんと別スレッドで処理しているかご確認ください。
ご記載のrunメソッドがUIスレッド(メインのスレッド)で動いていると、その中から
投げているHandlerは後回しになる(少なくともrunメソッドの中にいる時点では処理されない)ため、
進捗状況が出ないと思います。

Threadであればstartメソッドやjava.util.concurrent.Executors周りのexecuteなどを使って
別スレッドで動かしていれば進捗が出る気がします。

------------------------------------------
EGAWA Takashi



2009年11月5日0:49 ma <cool...@v001.vaio.ne.jp>:
Message has been deleted

hidaka

unread,
Nov 4, 2009, 8:04:43 PM11/4/09
to Android-SDK-Japan
maさん

日高です。

恐らくこういう事だろうと思われるサンプルソースを作成してみました。
https://sites.google.com/site/androidfolder335/ListProgressTest.zip?attredirects=0&d=1
原因は
・progressをnewしてしまっている。
 ここはレイアウトファイルを使用しているので、findViewByIdを使用
・progress.setMax(int)でMAX値を設定していない

この2点を解消すれば大丈夫だと思います。


以上

---------------------------------------
Msako Hidaka

On 11月5日, 午前9:33, egg <t.eg...@gmail.com> wrote:
> maさん
>
> 江川です。
> 新たなスレッドを開始する部分のソースが含まれていなかったので、
> 正確なことはわからないのですが、ちゃんと別スレッドで処理しているかご確認ください。
> ご記載のrunメソッドがUIスレッド(メインのスレッド)で動いていると、その中から
> 投げているHandlerは後回しになる(少なくともrunメソッドの中にいる時点では処理されない)ため、
> 進捗状況が出ないと思います。
>
> Threadであればstartメソッドやjava.util.concurrent.Executors周りのexecuteなどを使って
> 別スレッドで動かしていれば進捗が出る気がします。
>
> ------------------------------------------
> EGAWA Takashi
>
> 2009年11月5日0:49 ma <coolw...@v001.vaio.ne.jp>:
> > どなたか、ご教授いただけますでしょうか。- 引用テキストを表示しない -
>
> - 引用テキストを表示 -

ma

unread,
Nov 5, 2009, 7:35:36 AM11/5/09
to Android-SDK-Japan
江川 さま

早速の返信ありがとうございました。

thread の開始ですが、onCreate メソッドの一番最後で「thread.start();」にて開始していましたが、書きもれていまし
た・・・
すみません、説明が足りていませんでした。

それと、handler につきましては、Run メソッド内の「handler.sendMessage(msg);」を実行したタイミングで、毎度
「handleMessage」メソッドが実行されていることは確認できていました。
handleMessage メソッド内の「progress.setProgress(total); 」の引数 total は値が更新されても進
捗バーの表示が更新されない状態です・・・


On 11月5日, 午前9:33, egg <t.eg...@gmail.com> wrote:
> maさん
>
> 江川です。
> 新たなスレッドを開始する部分のソースが含まれていなかったので、
> 正確なことはわからないのですが、ちゃんと別スレッドで処理しているかご確認ください。
> ご記載のrunメソッドがUIスレッド(メインのスレッド)で動いていると、その中から
> 投げているHandlerは後回しになる(少なくともrunメソッドの中にいる時点では処理されない)ため、
> 進捗状況が出ないと思います。
>
> Threadであればstartメソッドやjava.util.concurrent.Executors周りのexecuteなどを使って
> 別スレッドで動かしていれば進捗が出る気がします。
>
> ------------------------------------------
> EGAWA Takashi
>
> 2009年11月5日0:49 ma <coolw...@v001.vaio.ne.jp>:

ma

unread,
Nov 5, 2009, 7:47:23 AM11/5/09
to Android-SDK-Japan
日高 さま

早速の返信ありがとうございます。

説明不足がありました。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/ITEM_TITLE"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
<ProgressBar
android:id="@+id/ITEM_PROGRESS"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal">
</ProgressBar>
</LinearLayout>

の、XMLファイルは、ListView の Row データとして使用したく、Activity 内の ListView の adapter のレ
イアウトとなっています。
なので、findViewById で Row データのオブジェクトを参照しようとしても、progress = (ProgressBar)
findViewById(R.id.ITEM_PROGRESS); ではオブジェクトが取れず、NULLが返却されてしまう状態です。

サンプルソースを書き換えて、全体をアップロードしてみました。
https://sites.google.com/site/androidexamples47/home/archive/ProgressOnListView.zip?attredirects=0&d=1


因みに、ListView の Row データ内に ProgressBar を入れる目的は、類似の処理を複数同時に行い、それぞれの進捗率を確認し
たいために実装したいと考えております。スタート画面の「start」ボタン → ListView の画面起動 → 1行進捗率表示 → 戻るボタ
ン → 「start」ボタン → ListView の画面起動 → 2行進捗率表示 となっています。

ひょっとしたら、BaseAdapter の拡張クラスに問題があるのでしょうか・・




On 11月5日, 午前10:04, hidaka <h.misa.3...@gmail.com> wrote:
> maさん
>
> 日高です。
>
> 恐らくこういう事だろうと思われるサンプルソースを作成してみました。https://sites.google.com/site/androidfolder335/ListProgressTest.zip?a...

hidaka

unread,
Nov 5, 2009, 10:18:16 PM11/5/09
to Android-SDK-Japan
ma さん

日高です。
まだソースは大体しか読めていないのですが、
findViewByIdでNULLが返却されてしまうのはインフレートをしていないからです。
理想としてはAdapterのgetView内でしているインフレートを
onCreateで行い、そのViewをaddする・・・といったイメージでしょうか。
せっかくHashMapに詰め込んでいるのに、getViewでまたインフレートしている・・・という状況になっているので。

getViewでは詰め込んだHashMapの値を参照してholderに渡してあげるほうがいいかなと思います。

データは入っているのにも関わらず、表示上更新されない理由も判り次第こちらに書かせて頂きます。


On 11月5日, 午後9:47, ma <coolw...@v001.vaio.ne.jp> wrote:
> 日高 さま
>
> 早速の返信ありがとうございます。
>
> 説明不足がありました。
>
> <?xml version="1.0" encoding="utf-8"?>
> <LinearLayout
> xmlns:android="http://schemas.android.com/apk/res/android"
> android:layout_width="wrap_content"
> android:layout_height="wrap_content"
> android:orientation="vertical">
> <TextView
> android:id="@+id/ITEM_TITLE"
> android:layout_width="wrap_content"
> android:layout_height="wrap_content">
> </TextView>
> <ProgressBar
> android:id="@+id/ITEM_PROGRESS"
> android:layout_width="fill_parent"
> android:layout_height="wrap_content"
> style="?android:attr/progressBarStyleHorizontal">
> </ProgressBar>
> </LinearLayout>
>
> の、XMLファイルは、ListViewの Row データとして使用したく、Activity 内のListViewの adapter のレ
> イアウトとなっています。
> なので、findViewById で Row データのオブジェクトを参照しようとしても、progress = (ProgressBar)
> findViewById(R.id.ITEM_PROGRESS); ではオブジェクトが取れず、NULLが返却されてしまう状態です。
>
> サンプルソースを書き換えて、全体をアップロードしてみました。https://sites.google.com/site/androidexamples47/home/archive/Progress...
>
> 因みに、ListViewの Row データ内に ProgressBar を入れる目的は、類似の処理を複数同時に行い、それぞれの進捗率を確認し
> たいために実装したいと考えております。スタート画面の「start」ボタン →ListViewの画面起動 → 1行進捗率表示 → 戻るボタ
> ン → 「start」ボタン →ListViewの画面起動 → 2行進捗率表示 となっています。
> > > >ListViewに追加する Row データのレイアウトは以下のように定義しています。
>
> > > > <?xml version="1.0" encoding="utf-8"?>
> > > > <LinearLayout
> > > > xmlns:android="http://schemas.android.com/apk/res/android"
> > > > android:layout_width="wrap_content"
> > > > android:layout_height="wrap_content"
> > > > android:orientation="vertical">
> > > > <TextView
> > > > android:id="@+id/ITEM_TITLE"
> > > > android:layout_width="wrap_content"
> > > > android:layout_height="wrap_content">
> > > > </TextView>
>
> > > > <ProgressBar
> > > > android:id="@+id/ITEM_PROGRESS"
> > > > android:layout_width="fill_parent"
> > > > android:layout_height="wrap_content"
> > > > style="?android:attr/progressBarStyleHorizontal">
> > > > </ProgressBar>
>
> > > > </LinearLayout>
>
> > > > BaseAdapter の拡張クラスを独自に作成し、以下のようにスレッド内で、ProgressBar の値を更新していますが、表示上の進捗率が
> > > > 全く更新されません。
>
> > > > protected void onCreate(Bundle savedInstanceState) {
> > > > super.onCreate(savedInstanceState);
>
> > > > listview= (ListView) findViewById(R.id.LIST);
> > > - 引用テキストを表示 -- 引用テキストを表示しない -
>
> - 引用テキストを表示 -

ma

unread,
Nov 6, 2009, 9:44:39 AM11/6/09
to Android-SDK-Japan
日高 さん

ma です


ご教示頂きました修正箇所を直したところ、全て意図するところの動作が確認できました^^


具体的な、修正箇所については、日高さんご指摘のとおりそのままなのですが、ご報告をいたします。

まず、findViewByIdでNULLが返却されてしまう問題

実は、Inflater について、私自身あまり理解していなかったのですが、今回ご指摘いただいたことで理解を深めることができました。
LayoutInflater の inflate メソッドを実行することで、自アクティビティ以外の外部定義のレイアウトが展開できるというこ
とでしょうか。

以下、アップロードしたサンプルコードの修正箇所を抜粋

LayoutInflater inflater;
inflater = LayoutInflater.from(ListViewAct.this);

View itemview;
itemview = inflater.inflate(R.layout.listitem, listview);

adapter = new ExtendsAdapter(ListViewAct.this);

listview.setAdapter(adapter);

progress = (ProgressBar) itemview.findViewById(R.id.ITEM_PROGRESS);

adapter.addView(itemview);


これで、Row データのレイアウトからオブジェクトを取得することができました。


HashMapに詰め込んでいるのに、getViewでまたインフレートしている問題

BaseAdapter の拡張クラスですが、恥ずかしながら、いろんなサンプルを見ながら流用しており、理解が浅い部分ではありました。
こちらもご指摘いただいたことで理解を深めることができました。

1. BaseAdapter の拡張クラス内で持つ HashMap は、View のオブジェクトだけにしました。

private HashMap<Integer, View> mHashView;

2. addData メソッドで、タイトルテキストとプログレスバーのオブジェクトを受け取っていましたが、ここも View オブジェクト一本
に修正

public void addView (View view) {
mHashView.put(new Integer(mHashView.size()), view);
}

3. getView 内の Inflate を廃止し、addView メソッドで追加された HashMap を参照し、
convertView に格納。
holder オブジェクトに convertView を設定。

convertView = (View) mHashView.get(position);
holder = new ViewHolder();
holder.view = convertView;
convertView.setTag(holder);


4. holder クラスのメンバを、View オブジェクトのみに修正

static class ViewHolder {
View view;
}

実は、ここ4日ほ程いろいろと試しては、はまっていたので、本当に感謝です!

日高 さん、江川さん、この度はご教示ありがとうございました。
また、質問を登校させていただくかもしれませんが、よろしくお願いいたします。

ma

unread,
Nov 6, 2009, 8:59:08 PM11/6/09
to Android-SDK-Japan
ma です

追加で質問させていただきたいのですが、ListView 内の進捗率が 100% に達し、スレッドが終了したタイミングで、
ListView からターゲットの Row アイテムを削除したいのですが、削除することができません。

Listview.removeView 等のメソッドで消せるのかと思っていましたが、違いますでしょうか?

ご教示の程、よろしくお願いいたします。
> > > > > > dlProgress = (int) ( (currentReadBytes / totalBytes) * 100);...
>
> もっと読む ≫

Keiji Ariyama

unread,
Nov 8, 2009, 11:00:12 AM11/8/09
to android-...@googlegroups.com
maさん

 有山@大阪です。ListViewから行を削除する方法ですが、ターゲットとなる
Rowアイテムは、どのようにして特定していますか?

 出来れば、実際のソースコードを示して貰えると答えやすいのですけど。
--
Keiji,
ml_an...@c-lis.co.jp

hidaka

unread,
Nov 8, 2009, 7:51:54 PM11/8/09
to Android-SDK-Japan
maさん

日高です。
恐らくUnsupportedExceptionが出てしまい、removeする事ができないのではないでしょうか?
Adapterをセットしているのにremoveやadd等、listviewに対して直接行おうとするとこのエラーが出てしまいます。

adapterのHashMap内のデータを消す事で解決できると思います。
mHashView.remove(アイテムのINDEX)
をスレッド終了のタイミングで呼び出すようにすれば大丈夫です。
> ...
>
> もっと読む ≫- 引用テキストを表示しない -
>
> - 引用テキストを表示 -

Keiji Ariyama

unread,
Nov 8, 2009, 9:46:35 PM11/8/09
to android-...@googlegroups.com
 あ、ソース有りましたね。失礼しました。有山@大阪です。

 日高さんの仰るとおり、アダプタ経由でリストを表示している時は、アダプタ
のソースを触るのが原則ですね。

 あと、ソースコードを見る限り、複数の行の進捗を別々に変化させるような仕
組みがないように思われますが、実現したい最終形ってどんなのをお考えなので
しょう。

 実際にはファイルのダウンロード中の画面のように、複数の進捗が並ぶのだと
考えてますが、もし、リストを読み込んでいる間の進捗表示のように、一つだけ
で良いのであれば、addFooterView(View v)とremoveFooterView(View v)を使っ
た方が、より手軽に目的を実現出来ると考えます。

有山
--
Keiji,
ml_an...@c-lis.co.jp

ma

unread,
Nov 9, 2009, 9:38:04 AM11/9/09
to Android-SDK-Japan
有山さん、日高さん

ma です。


ご教示ありがとうございます。


まず、adapterのHashMap内のデータを消すロジックを追加いたしました。

ExtendsAdapter の addView メソッド内で、追加された Hashmap の mHashView キー値をクラス内のプロパ
ティに
格納し、保持させました。

ターゲット Row の position は、これを参照。


スレッドの終了時に、mHashView.remove(アイテムのINDEX) をコールする実装を入れました。


上記の修正をいれ、実行したところ、上から順序良くスレッドが終了した場合には、ListView の一番上から
きれいに削除され正常な動きをするのですが、スレッドの終了が、最終行や、真ん中の行から終了した場合、うまく Row データが
消えてくれません。


修正ソースをアップロードいたしました。
https://sites.google.com/site/androidexamples47/home/archive/ProgressOnListView_2.zip?attredirects=0&d=1


mHashView を削除した後、インデックスの並び替えみたいなものが必要だったりするのでしょうか?

ご教示いただけますよう、何卒よろしくお願いいたします。
> >>>>>>>> 進捗状況が出ないと思います。...
>
> もっと読む ≫

hidaka

unread,
Nov 9, 2009, 9:04:06 PM11/9/09
to Android-SDK-Japan
ma さん

日高です。

長文で判りづらいかもしれませんが、原因から先に説明させて頂きます。

Adapterに対して3つのスレッドが登録されていると仮定します。

remove(2番目)を呼び出すと、3番目のデータは2番目があった場所にズレる必要がある。

しかし、getViewでconvertViewの値に前回表示された2番目のデータが残ってしまっている為、if文を通らずにいつまでも
convertViewが天下を取り続けてしまう。


回避策は

HashMap→Listに切り替え、removeの引数に対してitemviewを送るようにします。
これで、positionの値がどう変化しようと、itemviewと同じ物を探し出して消去してくれるので問題ありません。
そして、convertViewが居座り続ける問題については、getViewのif文をconvertView==null ではなく、!
viewList.get(position).equals(convertView)に切り替えます。
こうすると、convertViewがnullの時も、removeでデータがズレた場合にも対応する事ができると思います。




On 11月9日, 午後11:38, ma <coolw...@v001.vaio.ne.jp> wrote:
> 有山さん、日高さん
>
> ma です。
>
> ご教示ありがとうございます。
>
> まず、adapterのHashMap内のデータを消すロジックを追加いたしました。
>
> ExtendsAdapter の addView メソッド内で、追加された Hashmap の mHashView キー値をクラス内のプロパ
> ティに
> 格納し、保持させました。
>
> ターゲット Row の position は、これを参照。
>
> スレッドの終了時に、mHashView.remove(アイテムのINDEX) をコールする実装を入れました。
>
> 上記の修正をいれ、実行したところ、上から順序良くスレッドが終了した場合には、ListView の一番上から
> きれいに削除され正常な動きをするのですが、スレッドの終了が、最終行や、真ん中の行から終了した場合、うまく Row データが
> 消えてくれません。
>
> 修正ソースをアップロードいたしました。https://sites.google.com/site/androidexamples47/home/archive/Progress...

ma

unread,
Nov 10, 2009, 9:39:19 AM11/10/09
to Android-SDK-Japan
日高さん

maです。


いつもありがとうございます。

度々の質問で申し訳ありません。恐縮です。

ご指摘いただきました、HashMap から List<View> への修正および、getView メソッドの修正を行い動作させて見ましたとこ
ろ、進捗率が100%に達した行から削除されるようにはなったのですが、今度は ListView の最終行以外の Row データ内プログレスバーの
描画が更新されなくなる現象が発生しております。


public class ExtendsAdapter extends BaseAdapter {

private List<View> viewList;

/**
* コンストラクタ
* @param context
*/
public ExtendsAdapter (Context context) {
viewList = new ArrayList<View>();
}

/** View 追加 */
public void addView (View view) {
viewList.add(view);
}

/** View 削除 */
public void removeView (View view) {
viewList.remove(view);
super.notifyDataSetChanged();
}

~~ 中略 ~~

public View getView (int position, View convertView, ViewGroup
parent) {

ViewHolder holder;

if (!viewList.get(position).equals(convertView)) {
convertView = (View) viewList.get(position);

holder = new ViewHolder();
holder.view = convertView;
convertView.setTag(holder);
}
else {
holder = (ViewHolder)convertView.getTag();
}

// 「ViewHolder」の更新
updateHolder(viewList, holder);

// 「convertView」を返して終了
return convertView;
}


いろいろと、試してはみて、getViewのif文をconvertView==null に戻すと、ListView 最終行より前の行の描画が更新
されるので、おそらく getView メソッド内の何かが起因しているものと思われますが、さらに考慮すべき点はありますでしょうか?

ご教示いただけますよう、何卒よろしくお願いいたします。
> > > >>>>>> android:layout_width="wrap_content"...
>
> もっと読む ≫

ma

unread,
Nov 15, 2009, 7:29:02 AM11/15/09
to Android-SDK-Japan
maです。

ためしに、以下のようなことをして見ました。


public void removeView (View view) {
removedList = view; ← 追加
viewList.remove(view);
super.notifyDataSetChanged();
}

public View getView (int position, View convertView, ViewGroup
parent) {
// 「ViewHolder」の初期化
// ※「ViewHolder」は内部クラス。
ViewHolder holder;

// 「convertView」が空か確認
if (convertView == null ) {
// 「convertView」が空なので初期化
convertView = (View) viewList.get(position);

// 「ViewHolder」を作成
holder = new ViewHolder();
holder.view = convertView;
// 「convertView」に「ViewHolder」を格納
convertView.setTag(holder);
}
else if (convertView.equals(removedList)) {
convertView = (View) viewList.get(position);

// 「ViewHolder」を作成
holder = new ViewHolder();
holder.view = convertView;
// 「convertView」に「ViewHolder」を格納
convertView.setTag(holder);

}
else {
// 「convertView」があるので「ViewHolder」を取得
holder = (ViewHolder)convertView.getTag();
}

// 「ViewHolder」の更新
updateHolder(viewList.get(position), holder);

// 「convertView」を返して終了
return convertView;
}

結果は、Row データの総数が2行の場合は100%に達した行から正常に削除されますが、Row データの総行数が3行以上になると、最後の一行が
残った際にプログレスバーの描画の更新が止まってしまうようになりました・・・

2行までと3行以上とで何が違うのでしょう・・・
う~む 謎です・・・

半ば挫折気味ですが、ListView の Row データにプログレスバーを実装したことがある方がいましたら、ご教授いただけませんでしょうか?
> > > > >>>> holder.view = convertView;...
>
> もっと読む ≫
Reply all
Reply to author
Forward
0 new messages