Help us understand the problem. What is going on with this article?

BaseAdapter で ViewHolder パターンを実装する

概要

BaseAdapter で ViewHolder を実装する方法について述べます。主に Spinner を扱う際に役立ちます。

なぜやるのか?

Spinner と BaseAdapter を組み合わせた実装だと、LayoutInfrater.inflate する行で Android Studio の警告が出ます。

waning.png

Unconditional layout inflation from view adapter: Should use View Holder pattern (use recycled view passed into this method as the second parameter) for smoother scrolling.

別にこれを放置してもビルドはできますし、Spinner でスクロールのパフォーマンスが致命的になるほど要素を詰め込むことはそうそうないので、直さなくても構わないのではないかと思ってしまいがちです。実際私もずっと放置していました。

(ちなみに、リンク先を開くと RecyclerView のドキュメント が出てきます)

……が、昨今では技術者の評価に GitHub のリポジトリーを見ることも増えてきているようです。こうした雑なコードを残しておくのは自身の今後に悪い影響を与えかねないため、直しておくべきでしょう。

というのは冗談です。ViewHolder パターン自体は普遍的なデザインパターンですので、覚えておいて損はしません。私も RecyclerView を使い始めるまでは何度か書いていました。もう数年前になるので完全に忘れていましたが、せっかくなので思い出します。

BaseAdapterで画像とテキストをListView表示

実装

1. ViewHolder クラスを定義

各 View を持つクラスを定義します。これは Kotlin だと簡単です。Item を受け取って属性を各 View に当てる関数もこのクラスに実装させると良いでしょう。

class ViewHolder(private val icon: ImageView, private val text: TextView) {

    fun bindItem(item: Item) {
        icon.setImageDrawable(AppCompatResources.getDrawable(context, item.iconId))
        text.setText(item.id)
    }

}

2. getView で convertView の状態による分岐を書く

2-1 convertView が null の時

以下の 1-6 の処理をします。

  1. Item を入れる Layout を inflate
  2. 要素 View を find
  3. ViewHolder を初期化
  4. view の tag に ViewHolder のインスタンスをセット
  5. viewHolder に item の属性を設定
  6. inflate した Layout を return
    if (convertView == null) {
        val view = inflater.inflate(LAYOUT_ID, parent, false)
        val icon = view.findViewById<ImageView>(R.id.image)
        val text = view.findViewById<TextView>(R.id.text)
        val viewHolder = ViewHolder(icon, text)
        view.tag = viewHolder
        viewHolder.bindItem(item)
        return view
    }

2-2 convertView が null でない時

Layout の inflate は不要なので、 tag にセットした ViewHolder を取り出して item の属性を View に当てればよいです。

    val viewHolder = (convertView.tag as ViewHolder)
    viewHolder.bindItem(item)
    return convertView

全体のコード

override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
    val item = items[position]

    if (convertView == null) {
        val view = inflater.inflate(LAYOUT_ID, parent, false)
        val icon = view.findViewById<ImageView>(R.id.image)
        val text = view.findViewById<TextView>(R.id.text)
        val viewHolder = ViewHolder(icon, text)
        view.tag = viewHolder
        viewHolder.bindItem(item)
        return view
    }

    val viewHolder = (convertView.tag as ViewHolder)
    viewHolder.bindItem(item)
    return convertView
}

おわりに

Spinner を扱う際に知っておくと有益な ViewHolder パターンの実装について紹介しました。Kotlin だと大分書きやすくなったという印象を受けました。そもそも Spinner 自体そんなに使わないかもしれないですが、忘れた時にこのドキュメントが役立つとちょっとうれしいです。


参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした