satohu20xx's diary

思ったことをつらつらと

ConvertViewの再利用の解釈

よくAdapterを使ったときにConvertViewの再利用がどーだこーだ書いてあるけど、解釈したことのメモ

ListViewを例にとって話をする。

    class ListAdapter extends ArrayAdapter<String>{
        public ListAdapter(Context context, String[] str) {
            super(context, 0, str);
        }

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

            if(convertView == null) {
                convertView = new TextView(getContext());
                ((TextView)convertView).setTextSize(32.f);
                convertView.setTag(position);
            }

            ((TextView)convertView).setText(getItem(position));

            Log.d("DEBUG", String.valueOf(position));
            Log.d("DEBUG", convertView.getTag().toString());

            return convertView;
        }
    }

ざっくりと検証用のコードを上みたいにかいてみたとして、動かすと以下の画面が出てくる。

f:id:satohu20xx:20120122214206p:plain

この画面が表示されるにはgetView()が11回呼ばれることになる。画面上に少しでもViewが出ていると判定されるとgetView()が呼ばれるみたい。んで、こいつをスクロールすると、12番目のオブジェクトが表示されることになる。1画面には11個のオブジェクトしか表示できる領域がないから12番目が表示されると0番目に表示されていたオブジェクトが見えなくなる。この見えなくなったーっていう判定をAndroidが勝手にやってくれるのがAdapterみたい。見えなくなったViewはメモリの無駄だから、この0番目のViewを使って12番目のViewを作るっていう仕組みがよく書いてある再利用という話。

Viewが再利用されると引数のconvertViewに見えなくなったView(今の例だと0番目のView)が設定されてgetViewが呼び出されることになる。convertViewがnullかどうかを判定してnullじゃなかったらわざわざnewすることなく使いまわしてやりましてやることでメモリが節約できて速度も上がって万々歳なんだとさ。

んで、よく画像を別のスレッドでダウンロードしてダウンロードし終わったらImageViewに設定してってことをやるんだけど、この再利用で0番目だったconvertViewがダウンロードしてる間にスクロールされて12番目のViewになってるとかがありえるわけさ。だからsetTagでなんらかの判別情報をいれといて再利用してるかどうかを判定してやることで別のイメージがダウンロードされても設定されないようにできるってことみたい。詳しい話は以下のURLを参考に

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

Android良く出来てる。