やりたいこと
なるべく頭を使わずに最短でRecyclerViewで複数レイアウトを使う。
いつも忘れる + ググった時にやり方色々出てきてややこしいのでテンプレート作っておく。
実装方法の大枠
基本的に最後に添付したコードをまるっとコピペして少し修正すればOK。
データやレイアウトは自由に作って試してみてください。
① ViewTypeのEnumを作る
② onCreateViewHolder
はコピペ
③ onBindViewHolder
もほぼコピペ
④ getItemViewType
もほぼコピペ
⑤ getItemCount
もほぼコピペ
⑥ ViewHolderのメソッドでデータとレイアウトを紐づける
※ 上記の順番で実装していく場合はViewHolder
がないよとエラーになりますが、最後までいけば解消されるのでスルーしてください。
元となるAdapterファイル
data class Layout1Data(
val country: String,
val city: String
)
data class Layout2Data(
val name: String,
val number: Int
)
class MultiLayoutRecyclerAdapter :
RecyclerView.Adapter<MultiLayoutRecyclerAdapter.ViewHolder>() {
private val list = listOf(
Layout1Data("Japan", "Tokyo"),
Layout2Data("Alice", 1),
Layout2Data("Bob", 2),
Layout2Data("Cathy", 3),
Layout2Data("Daniel", 4),
Layout2Data("Elza", 5),
Layout2Data("Fiona", 6),
Layout2Data("Gina", 7),
Layout2Data("Helen", 8),
)
}
色々な実装方法があってややこしいので、下記の$クラス
だけ変更すればOKになるように実装していきます。
class $クラス: RecyclerView.Adapter<$クラス.ViewHolder>()
① ViewTypeのEnumを作る
private enum class ViewType(val layoutId: Int) {
LAYOUT_1(R.layout.layout_1),
LAYOUT_2(R.layout.layout_2),
}
viewTypeに応じてレイアウトを出し分けたいのでとプロパティにレイアウトIDを設定しておきます。
なおprivateにしているのでクラス名("ViewType")をファイルごとに変更する必要はありません。
② onCreateViewHolder
はコピペ
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val view: ViewDataBinding = DataBindingUtil.inflate(
LayoutInflater.from(viewGroup.context),
ViewType.values()[viewType].layoutId,
viewGroup,
false
)
return ViewHolder(view)
}
①で作成したEnumを使用してコピペを実現します。
ViewType.values()[viewType].layoutId
でviewTypeに応じたレイアウトIDを指定しています。
③ onBindViewHolder
もほぼコピペ
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
when(val data = list[position]) {
is Layout1Data -> viewHolder.bind(data)
is Layout2Data -> viewHolder.bind(data)
else -> return
}
}
listに渡したデータに応じてそれぞれのレイアウトにデータをバインドしていきます。
④ getItemViewType
もほぼコピペ
override fun getItemViewType(position: Int): Int {
return when(list[position]) {
is Layout1Data -> ViewType.LAYOUT_1.ordinal
is Layout2Data -> ViewType.LAYOUT_2.ordinal
else -> ViewType.LAYOUT_2.ordinal
}
}
listに渡したデータに応じてItemViewのViewTypeを指定します。
⑤ getItemCount
もコピペ
override fun getItemCount() = list.size
listのサイズを指定します
⑥ ViewHolderのメソッドでデータとレイアウトを紐づける
inner class ViewHolder(private val binding: ViewDataBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(data: Layout1Data) {
(binding as Layout1Binding).run {
country.text = data.country
city.text = data.city
}
}
fun bind(data: Layout2Data) {
(binding as Layout2Binding).run {
name.text = data.name
name.setTextColor(Color.BLUE)
number.text = data.number.toString()
}
}
}
fun bind(data: $データクラス)
をレイアウトの数だけ用意します。
各bindメソッド内でレイアウトとデータを紐づけていけば完了です。
なお、innerにしてるのでクラス名("ViewHolder")をファイルごとに変更する必要はありません。
コピペ用
data class Layout1Data(
val country: String,
val city: String
)
data class Layout2Data(
val name: String,
val number: Int
)
class MultiLayoutRecyclerAdapter :
RecyclerView.Adapter<MultiLayoutRecyclerAdapter.ViewHolder>() {
private val list = listOf(
Layout1Data("Japan", "Tokyo"),
Layout2Data("Alice", 1),
Layout2Data("Bob", 2),
Layout2Data("Cathy", 3),
Layout2Data("Daniel", 4),
Layout2Data("Elza", 5),
Layout2Data("Fiona", 6),
Layout2Data("Gina", 7),
Layout2Data("Helen", 8),
)
private enum class ViewType(val layoutId: Int) {
LAYOUT_1(R.layout.layout_1),
LAYOUT_2(R.layout.layout_2),
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val view: ViewDataBinding = DataBindingUtil.inflate(
LayoutInflater.from(viewGroup.context),
ViewType.values()[viewType].layoutId,
viewGroup,
false
)
return ViewHolder(view)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
when(val data = list[position]) {
is Layout1Data -> viewHolder.bind(data)
is Layout2Data -> viewHolder.bind(data)
else -> return
}
}
override fun getItemViewType(position: Int): Int {
return when(list[position]) {
is Layout1Data -> ViewType.LAYOUT_1.ordinal
is Layout2Data -> ViewType.LAYOUT_2.ordinal
else -> ViewType.LAYOUT_2.ordinal
}
}
override fun getItemCount() = list.size
inner class ViewHolder(private val binding: ViewDataBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(data: Layout1Data) {
(binding as Layout1Binding).run {
country.text = data.country
city.text = data.city
}
}
fun bind(data: Layout2Data) {
(binding as Layout2Binding).run {
name.text = data.name
name.setTextColor(Color.BLUE)
number.text = data.number.toString()
}
}
}
}