GridViewに非同期で取得した画像がスクロール時に入れ替わる

1,564 views
Skip to first unread message

zunda

unread,
Feb 12, 2012, 8:56:24 PM2/12/12
to 日本Androidの会
はじめまして、zundaと申します。

現在、Twitter APIを使用したアプリを開発しているのですが、取得した画像リストをGridViewに表示する、という処理で以下の2点がど
うにもうまくいかず困っております。

・0番目の画像が他のところにも表示される
・スクロール時に画像が入れ替わる(データの再利用時に発生?)

以下、GridViewのアダプタクラスのgetViewの部分です。

------------------------------------------------------------------------------------

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

if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
// Get Tweet
Tweet tweet = bList.get(position);
// Get Media URL
URL url = tweet.getMediaEntities()[0].getMediaURL();
// Initialize
convertView = inflater.inflate(R.layout.grid_item, null);
// Set Tag
convertView.findViewById(R.id.imageView).setTag(url.getPath());
// Deode Task
ImageDecodeTask decodeTask = new ImageDecodeTask(convertView);
decodeTask.execute(url);
}

return convertView;
}

------------------------------------------------------------------------------------

画像の読み込みをAsyncTaskを使用して1画像につき1タスクで非同期処理を行っていることが原因と思い、以下のブログなどを参考に試行錯誤して
みましたが、問題の現象がどうしても解消されません。
http://slumbers99.blogspot.com/2011/08/android-listview.html

デバッグしてみたところ、getView内でただconvertViewを返すだけで別の画像に入れ替わっているようです。
(ImageDecodeTaskが呼ばれなくても入れ替わる)

どなたか解決法をご存知の方がいらっしゃいましたら、ご教授頂ければ幸いです。
よろしくお願い致します。

noxi

unread,
Feb 12, 2012, 9:18:05 PM2/12/12
to android-g...@googlegroups.com
noxiです。


参照されているブログ記事中にもあります通りconvertViewは再利用されるため
その書き方だと再利用されたViewがconvertViewにセットされていた場合が全く考慮されていません。

http://y-anz-m.blogspot.com/2010/08/androidthe-world-of-listview.html
この辺も参考にして他の方がどう考えているのかをもっと理解した方が良いと思います。
またViewが最利用されることを考慮し取得する画像や文字列はViewとは別に管理することをお勧めします。


getView内でAsyncTaskを何の考慮も無く呼んでしまうと
スレッド数の制限により落ちる場合がある様なので注意して下さい。
http://319ring.net/blog/archives/1707


2012年2月13日10:56 zunda <javate...@gmail.com>:

> --
> このメールは Google グループのグループ「日本Androidの会」の登録者に送られています。
> このグループに投稿するには、android-g...@googlegroups.com にメールを送信してください。
> このグループから退会するには、android-group-j...@googlegroups.com にメールを送信してください。
> 詳細については、http://groups.google.com/group/android-group-japan?hl=ja からこのグループにアクセスしてください。
>

Makoto Yamazaki

unread,
Feb 12, 2012, 11:59:54 PM2/12/12
to android-g...@googlegroups.com
zaki です。

convertViewに渡されてくる View は、「ListView の要素として以前は表示に使っていた
けれども、今はもう表示されていないので再利用できるなら使ってください」という
ものです。
なので、実行速度(スクロールの滑らかさ)を考えなければconvertView を無視して毎回
inflate すればただしく動きます。これだと効率が悪い(頻繁に View のinflateが必要になる)
ので、通常はconvertView が null でない場合は convertView を使いまわす実装をします。

このような使い回しを実装する場合は、今回のように ViewをAsyncTask に渡して参照を
保持させるような使い方をするとAsyncTask が完了する前に View が再利用されて別の
AsyncTaskに渡される可能性があることを考慮する必要があります。
例えばAsyncTask 実行の際にURLを覚えておき、イメージがロードし終わったあとで
ImageView#getTag()が返す URL がロードしたものと一致することをチェックする
方法などが考えられます。


2012/2/13 noxi <android.w...@gmail.com>:

--
YAMAZAKI Makoto

zunda

unread,
Feb 13, 2012, 2:53:31 AM2/13/12
to 日本Androidの会
zundaです。

noxiさん、zakiさん、ご回答ありがとうございます。

convertViewとして返ってくる値について、私がきちんと理解できていませんでした。
再利用された値が正しい値(こちらの意図する値)とは限らないのですね。

noxiさんのアドバイスにあるように、表示する画像とpositionをMapとして別に管理するようにしたところ、すべての画像が正しく表示され、
スクロールしても画像が入れ替わらなくなりました。

これに加えて、zakiさんのアドバイスのようにAsyncTask側でも受け取ったのが正しいキューなのかを判断する処理を加えようと思います。

ただ、noxiさんの仰るとおり、読み込み数が多くなると落ちるという状態が発生していますので、スレッド数を一定数以下に管理できるような仕組みを考
えてみようと思います。

お2方とも、親切なご回答ありがとうございました。
Reply all
Reply to author
Forward
0 new messages