前提・実現したいこと
Android Studio の RecyclerView で複数タイプの ViewHolder を追加したり削除したりしても問題なく表示できるようにしたい。
発生している問題・エラーメッセージ
RecyclerView の View をタップするとそのViewが削除されるような機能があるとして、下記の Adapter で定義した remove メソッドで削除すると onBindViewHolder で position と ViewHolder の情報が合わなくなりうまく表示できません。例として 9: Red の View をタップして削除すると、2枚目の添付画像のように 10: Orange が下に2つ表示されてしまいます。
該当のソースコード
MainActivity.kt
kotlin
1class MainActivity : AppCompatActivity() { 2 private lateinit var binding: ActivityMainBinding 3 private var adapter = ListAdapter() 4 5 override fun onCreate(savedInstanceState: Bundle?) { 6 super.onCreate(savedInstanceState) 7 binding = ActivityMainBinding.inflate(layoutInflater) 8 setContentView(binding.root) 9 10 // 30個のデータ生成 11 var colors = mutableListOf<Color>() 12 13 for (i in 1..30) { 14 val color = Color() 15 color.id = i 16 17 if (i % 3 == 0) { 18 color.type = ColorType.RED 19 } 20 if (i % 3 == 1) { 21 color.type = ColorType.ORANGE 22 } 23 if (i % 3 == 2) { 24 color.type = ColorType.YELLOW 25 } 26 27 colors.add(color) 28 } 29 30 adapter.models = colors 31 32 binding.list.adapter = adapter 33 binding.list.layoutManager = LinearLayoutManager(this) 34 } 35}
ListAdapter.kt
kotlin
1package com.example.tableapp 2 3import android.graphics.drawable.ColorDrawable 4import android.view.LayoutInflater 5import android.view.View 6import android.view.ViewGroup 7import android.widget.TextView 8import androidx.recyclerview.widget.RecyclerView 9 10class Color { 11 var id: Int = 0 12 var type: ColorType = ColorType.RED 13} 14 15enum class ColorType(val colorName: String, val code: String) { 16 RED("Red", "#c0392b"), 17 ORANGE("Orange", "#e67e22"), 18 YELLOW("Yellow", "#f1c40f") 19} 20 21sealed class ListViewHolder(view: View) : RecyclerView.ViewHolder(view) { 22 companion object { 23 operator fun invoke(color: Color, inflater: LayoutInflater, parent: ViewGroup) = 24 when (color.type) { 25 ColorType.RED -> { 26 RedView(inflater.inflate(R.layout.fragment_red, parent, false)) 27 } 28 ColorType.ORANGE -> { 29 OrangeView(inflater.inflate(R.layout.fragment_orange, parent, false)) 30 } 31 else -> { 32 YellowView(inflater.inflate(R.layout.fragment_yellow, parent, false)) 33 } 34 } 35 } 36} 37 38class RedView(view: View) : ListViewHolder(view) { 39 val titleView: TextView = view.findViewById(R.id.text) 40 41 fun bind(color: Color, onClick: (View) -> Unit) { 42 titleView.text = "${color.id}: Red" 43 itemView.background = ColorDrawable(android.graphics.Color.parseColor(color.type.code)) 44 itemView.setOnClickListener(onClick) 45 } 46} 47 48class OrangeView(view: View) : ListViewHolder(view) { 49 val titleView: TextView = view.findViewById(R.id.text) 50 51 fun bind(color: Color, onClick: (View) -> Unit) { 52 titleView.text = "${color.id}: Orange" 53 itemView.background = ColorDrawable(android.graphics.Color.parseColor(color.type.code)) 54 itemView.setOnClickListener(onClick) 55 } 56} 57 58class YellowView(view: View) : ListViewHolder(view) { 59 val titleView: TextView = view.findViewById(R.id.text) 60 61 fun bind(color: Color, onClick: (View) -> Unit) { 62 titleView.text = "${color.id}: Yellow" 63 itemView.background = ColorDrawable(android.graphics.Color.parseColor(color.type.code)) 64 itemView.setOnClickListener(onClick) 65 } 66} 67 68class ListAdapter : RecyclerView.Adapter<ListViewHolder>() { 69 var models = mutableListOf<Color>() 70 var selectItemListener: ((position: Int) -> Unit)? = null 71 72 init { 73 setHasStableIds(true) 74 } 75 76 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder { 77 val inflater = LayoutInflater.from(parent.context) 78 val color: Color = models[viewType] 79 80 return ListViewHolder(color, inflater, parent) 81 } 82 83 override fun onBindViewHolder(holder: ListViewHolder, position: Int) { 84 val color: Color = models[position] 85 86 when (color.type) { 87 ColorType.RED -> { 88 if (holder is RedView) { 89 holder.bind(color, onClick = { 90 remove(holder.layoutPosition) 91 }) 92 } 93 } 94 ColorType.ORANGE -> { 95 if (holder is OrangeView) { 96 holder.bind(color, onClick = { 97 remove(holder.layoutPosition) 98 }) 99 } 100 } 101 ColorType.YELLOW -> { 102 if (holder is YellowView) { 103 holder.bind(color, onClick = { 104 remove(holder.layoutPosition) 105 }) 106 } 107 } 108 else -> { 109 } 110 } 111 } 112 113 override fun getItemId(position: Int): Long { 114 val color = models[position] 115 return color.id.toLong() 116 } 117 118 override fun getItemViewType(position: Int): Int = position 119 override fun getItemCount(): Int = models.size 120 121 fun add(color: Color, position: Int) { 122 models.add(position, color) 123 notifyItemInserted(position) 124 } 125 126 fun remove(position: Int) { 127 models.removeAt(position) 128 notifyItemRemoved(position) 129 } 130}
【1枚目】
【2枚目】
試したこと
RecyclerView のリストの追加・削除方法は一通り試したと思うのですがどれもうまくいかず。notifyDataSetChanged でもだめでした。
そもそも上記の削除方法が間違っているのか、RecyclerView の使い方が間違っているのかもわかっていないので、よろしければ教えていただけると助かります。
補足情報(FW/ツールのバージョンなど)
Android Studio 4.2.2
追記
activity_main.xml
xml
1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context=".MainActivity"> 8 9 <androidx.recyclerview.widget.RecyclerView 10 android:id="@+id/list" 11 android:layout_width="0dp" 12 android:layout_height="0dp" 13 app:layout_constraintBottom_toBottomOf="parent" 14 app:layout_constraintEnd_toEndOf="parent" 15 app:layout_constraintStart_toStartOf="parent" 16 app:layout_constraintTop_toTopOf="parent" /> 17</androidx.constraintlayout.widget.ConstraintLayout>
fragment_red.xml
xml
1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:id="@+id/constraintLayout" 6 android:layout_width="match_parent" 7 android:layout_height="60dp" 8 tools:context=".RedFragment"> 9 10 <!-- TODO: Update blank fragment layout --> 11 <TextView 12 android:id="@+id/text" 13 android:layout_width="0dp" 14 android:layout_height="wrap_content" 15 android:text="@string/hello_blank_fragment" 16 app:layout_constraintEnd_toEndOf="parent" 17 app:layout_constraintStart_toStartOf="parent" 18 app:layout_constraintTop_toTopOf="parent" /> 19 20</androidx.constraintlayout.widget.ConstraintLayout>
fragment_orange.xml
xml
1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:id="@+id/constraintLayout" 6 android:layout_width="match_parent" 7 android:layout_height="60dp" 8 tools:context=".OrangeFragment"> 9 10 <!-- TODO: Update blank fragment layout --> 11 <TextView 12 android:id="@+id/text" 13 android:layout_width="0dp" 14 android:layout_height="wrap_content" 15 android:text="@string/hello_blank_fragment" 16 app:layout_constraintEnd_toEndOf="parent" 17 app:layout_constraintStart_toStartOf="parent" 18 app:layout_constraintTop_toTopOf="parent" /> 19 20</androidx.constraintlayout.widget.ConstraintLayout>
fragment_yellow.xml
xml
1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:id="@+id/constraintLayout" 6 android:layout_width="match_parent" 7 android:layout_height="60dp" 8 tools:context=".YellowFragment"> 9 10 <!-- TODO: Update blank fragment layout --> 11 <TextView 12 android:id="@+id/text" 13 android:layout_width="0dp" 14 android:layout_height="wrap_content" 15 android:text="@string/hello_blank_fragment" 16 app:layout_constraintEnd_toEndOf="parent" 17 app:layout_constraintStart_toStartOf="parent" 18 app:layout_constraintTop_toTopOf="parent" /> 19 20</androidx.constraintlayout.widget.ConstraintLayout>
回答1件
あなたの回答
tips
プレビュー