Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@toastkidjp

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

More than 1 year has passed since last update.

概要

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


参考

1
Help us understand the problem. What is going on with this article?
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
toastkidjp
Web 系企業の Android アプリ開発者です。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?