47608


AndroidのRecyclerView.Adapterの実装において、2種類以上のViewTypeを扱う場合には `onBindViewHolder`
の実装内で場合分けをする必要があると思っています。

一般的なサンプルコードでは、たとえば以下のような実装が成されていると認識しています:

[code]

    // in Kotlin
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        when (getItemViewType(position)) {
            VIEW_TYPE_MY_STANDARD -> {
                (holder as MyStandardViewHolder) // do something
            }
            VIEW_TYPE_MY_SUB -> {
                (holder as MySubViewHolder) // do something
            }
            else -> throw IllegalStateException()
        }
    }
    
[/code]

対しチームメンバーのコードが、以下のようになっておりました:

[code]

    // in Kotlin
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        when (holder) {
            is MyStandardViewHolder -> {
                // do something
            }
            is MySubViewHolder -> {
                // do something
            }
            else -> throw IllegalStateException()
        }
    }
    
[/code]

このコードは、少なくともプロジェクト内では期待どおりの挙動であることを確認しています。

もし何か想定外の動作をする可能性があればエッジケース含め認識しておきたいと思っているのですが、何か起こりうる問題はありますでしょうか？  
また問題が起こり得なければ、そのまま後者のコードを採用したいです。


CC BY-SA 4.0
2018-08-15T01:59:44.453
47527
2018-08-18T19:21:48.003
23873
question
android kotlin
RecyclerView.Adapter の onBindViewHolder ではViewType判別に「getItemViewType」と「holderをinstanceofで判定」のどちらを用いるのが良いか (Android)


viewTypeとViewHolderの定義が1対1の対応となっているならば、後者のコードで問題ないと思います。

あまり考えにくいですが、1つのViewHolderに対しviewTypeが複数定義されている場合などは問題になりうるかもしれません。（同じレイアウトだけどどうしてもViewを使い回しさせたくない場合とか）


CC BY-SA 4.0
2018-08-18T13:18:59.610
47608
2018-08-18T13:18:59.610
915
47527
answer


実用上の問題としてcastに失敗することが考えられないのであれば、後者の実装でも問題ありません。

そのような杞憂をなくしたい場合、例えば`enum class`や`sealed class`を使う方法があります。

Sealed Classes - Kotlin Programming Language  
<https://kotlinlang.org/docs/reference/sealed-classes.html>

ただし、`sealed
class`は定義されてるktファイル内でしか参照できないので、全てのViewHolderの実装を1つのktファイル内に定義する必要が出てきます。

**MyViewHolder.kt**

[code]

    sealed class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
    
    class MyStandardViewHolder(val binding: ItemStandardBinding) : MyViewHolder(binding.root)
    
    class MySubViewHolder (val binding: ItemSubBinding) : MyViewHolder(binding.root)
    
[/code]

**MyAdapter.kt**

[code]

    class MyAdapter: RecyclerView.Adapter<MyViewHolder>() {
    
        // 中略
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder{
            val inflater = LayoutInflater.from(parent.context)
            val type = MyViewType.values()[viewType]
    
            return when (type) {
                STANDARD ->
                    MyStandardViewHolder(DataBindingUtil.inflate(inflater, R.layout.item_standard, parent, false))
                SUB ->
                    MySubViewHolder(DataBindingUtil.inflate(inflater, R.layout.item_sub, parent, false))
                // enum classなので、分岐が網羅されていれば、elseは不要
            }
        }
    
        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            when (holder) {
                is MyStandardViewHolder -> {
                    // do something
                }
                is MySubViewHolder -> {
                    // do something
                }
                // seald class MyViewHolderのsub classは、上記2つ以外に存在しないこと(網羅されてること)を
                // コンパイラが知ってるので、elseが不要になる
            }
        }
    
        override fun getItemViewType(position: Int): Int {
            // 何かしらの条件によって切り替える
            return if (standard) {
                STANDARD.ordinal
            } else {
                SUB.ordinal
            }
        }
    
        // 中略
    
    }
    
    enum class MyViewType {
        STANDARD,
        SUB;
    }
    
[/code]

`enum class`に関しては、`throw
IllegalStateException()`が不要になる代わりに、`MyViewType.values()[viewType]`で`IndexOutOfBoundsException`が発生する杞憂が増えるので、あまり本質的な解決方法ではないかもしれませんが、一応の例としてご参考ください。


CC BY-SA 4.0
2018-08-18T19:01:54.603
47613
2018-08-18T19:21:48.003
2018-08-18T19:21:48.003
3068
3068
47527
answer