LoginSignup
3
2

More than 3 years have passed since last update.

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

Posted at

概要

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 自体そんなに使わないかもしれないですが、忘れた時にこのドキュメントが役立つとちょっとうれしいです。


参考

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2