200
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

ViewHolderを使わないでListViewを高速化する

概要

一般的なListViewの高速化手法としてViewHolderというものがあります。

これはAdapter#getView()でアイテムのfindViewById()の回数を減らす(生成済みのものを使いまわす)ことで速度を改善するというもの。

今回はこのViewHolderを使わないでListViewを高速化する方法を書きます。

その方法はズバリ、Custom Viewを使う方法です!!

順に説明していきます。

コードはGitHubに上げていますので、参考にしてみてください。

mofumofu3n/NonViewHolder

アイテムをカスタムViewにする

ListViewのアイテムのレイアウトファイルと実装を書いていきます。

アイテムのルートレイアウトの名前はItemLayout.javaとしています。

レイアウトファイル

タイトル、概要、アイコンを持ったアイテムを想定しています。

<?xml version="1.0" encoding="utf-8"?>
<com.example.nonviewholder.ItemLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView 
        android:id="@+id/titleView"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
         />

    <TextView
        android:id="@+id/descriptionView" 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />

    <ImageView
        android:id="@+id/iconView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:drawable/ic_dialog_alert"
        />

</com.example.nonviewholder.ItemLayout>

ItemLayoutの実装

アイテムのレイアウトはLinearLayoutを継承して実装しています。

ここでのポイントはonFinishInflate()が呼ばれた時にfindViewById()することです。

こうすることでAdapterで新しくアイテムのレイアウトを生成するときにだけfindViewById()されます。

まさにViewHolderと同様の効果が得られます。

public class ItemLayout extends LinearLayout {
    // タイトル
    TextView mTitleView;
    // 概要
    TextView mDescriptionView;
    // アイコン
    ImageView mIconView;

    public ItemLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        mTitleView = (TextView) findViewById(R.id.titleView);
        mDescriptionView = (TextView) findViewById(R.id.descriptionView);
        mIconView = (ImageView) findViewById(R.id.iconView);
    }

    public void bindView(Item item) {
        mTitleView.setText(item.title);
        mDescriptionView.setText(item.description);
        mIconView.setImageResource(item.icon);
    }
}

Adapterの処理

アダプターの処理はViewHolderを利用する時よりも簡単にかけます。

getView()の動きはほぼ一緒ですがViewHolderと同様の処理を

Custom Viewの方で行っているため、レイアウトの生成(or 再利用)とアイテムをレイアウトに渡すだけで済みます。

public class CustomItemAdapter extends ArrayAdapter<Item> {
    private LayoutInflater mFactory;
    private int mItemLayoutResource;

    public CustomItemAdapter(Context context, int resource, List<Item> objects) {
        super(context, resource, objects);

        mFactory = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mItemLayoutResource = resource;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final ItemLayout view;

        if (convertView == null) {
            // Viewがなかったら生成
            view = (ItemLayout) mFactory.inflate(mItemLayoutResource, null);
        } else {
            view = (ItemLayout) convertView;
        }

        view.bindView(getItem(position));

        return view;
    }
}

ViewHolderとの比較

ViewHolderを利用した時のAdapterはこちら。

Viewに対する処理がAdapterに依存するので、見通しが悪いです。(工夫の余地はあるかもしれません)

AdapterにViewの処理を書くよりもCustom Viewにアイテムだけ渡しそちらでViewの処理を任せたほうが
Adapterの肥大化が防げます。

public class ItemAdapter extends ArrayAdapter<Item> {
    private LayoutInflater mFactory;
    private int mItemLayoutResource;

    public ItemAdapter(Context context, int resource, List<Item> objects) {
        super(context, resource, objects);

        mFactory = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mItemLayoutResource = resource;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final ViewHolder holder;

        if (convertView == null) {
            convertView = mFactory.inflate(mItemLayoutResource, null);
            holder = new ViewHolder();
            holder.title = (TextView) convertView.findViewById(R.id.titleView);
            holder.desc = (TextView) convertView.findViewById(R.id.descriptionView);
            holder.icon = (ImageView) convertView.findViewById(R.id.iconView);

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

        final Item item = getItem(position);

        holder.title.setText(item.title);
        holder.desc.setText(item.description);
        holder.icon.setImageResource(item.icon);

        return convertView;
    }

    class ViewHolder {
        TextView title;
        TextView desc;
        ImageView icon;
    }
}

まとめ

ViewHolderを使わないでListViewを高速化する方法を書いてみました。

個人的にはTextViewに文字をセットする等のViewを操作する処理をCustom Viewに任せられ、

Adapterの見通しもよくなるため、ViewHolderよりもCustom Viewの方が好きです。

ViewHolderでもっと良い書き方あるよ!と言う方がいらっしゃいましたら、

ぜひコメントに書いて下さい。

あと、個人的なことですがQiitaに3日連続で投稿しています。

そろそろ投稿するネタが尽きてきました。。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
200
Help us understand the problem. What are the problem?