2011/08/16

Android で ListView に非同期で取ってきた画像を表示したら位置がおかしい件

あ…ありのまま 今 起こった事を話すぜ!
『一番目のアイコンを設定したとおもったら、5番目のアイコンも設定していた』
な… 何を言ってるのか(ry

でもね、ほんとにそう思いました。

Twitterとかによくみられる「左にアイコンがあってその右に文字」のレイアウト。
それを表現するために、Android だと ListView がまあよく使われると思います。
盛大にハマったので一応な解決策です。




1にしか画像ファイルはないんだけど・・・・
下にスクロールすると6にも7にも・・・
グリグリしてるとなにがなにやら・・・・
たぶん原因としては、 ListView 内で contentview を使いまわしてるから、
非同期で取ってくると参照がずれて違うところに当てはめちゃうっていうことだと思う。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ListItem listItem = (ListItem) getItem(position);
ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(resourceId, null);
holder = new ViewHolder();
holder.thumbnail = (ImageView) convertView.findViewById(R.id.thumbnail);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.explain = (TextView) convertView.findViewById(R.id.explain);
holder.nextButton = (ImageView) convertView.findViewById(R.id.nextButton);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// 画像を非表示
holder.thumbnail.setVisibility(View.GONE);
// ImageView にタグを設定
// このタグを AsyncTask で使います。
holder.thumbnail.setTag(listItem.thumbnailUrl);
// 非同期通信開始
DownloadTask task = new DownloadTask(holder.thumbnail);
task.execute(listItem.thumbnailUrl);
// テキストは普通にセットする
holder.title.setText(listItem.title);
holder.explain.setText(listItem.explain);
return convertView;
}
view raw adapter.java hosted with ❤ by GitHub
class DownloadTask extends AsyncTask<String, Drawable, Void> {
private ImageView imageView;
private String tag;
public DownloadTask(ImageView imageView) {
this.imageView = imageView;
// ImageView に設定したタグをメンバへ
this.tag = imageView.getTag().toString();
}
@Override
protected Drawable doInBackground(String... urls) {
synchronized (context) {
try {
Drawable image = ImageCache.get(urls[0]);
if (image == null) {
URL url = new URL(urls[0]);
image = Drawable.createFromStream((InputStream) url.getContent(), "");
ImageCache.set(urls[0], image);
}
return image;
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
@Override
protected void onPostExecute(Drawable result) {
// メンバのタグと imageView にセットしたタグが一致すれば
// 画像をセットする
if (this.tag.equals(this.imageView.getTag())) {
if (result != null) {
this.imageView.setImageDrawable(result);
this.imageView.setVisibility(View.VISIBLE);
}
}
}
}

いろいろ関係ない処理が入ってますが、そこはご愛嬌。
ImageView のタグと、AsyncTask のタグと比較することで同じ行と判断し、
画像を表示することができました。
再利用されているってわかっても、どうも解決策が見つからなくて、
激ハマリしました。


※参考にさせて頂きました。
Android画像付きリストの設定(ListView) らぼ☆ろぐ