RecyclerView の使い方。ドラッグ&ドロップで並び替え、スワイプで削除する。【Android】

RecyclerView の基本的な使い方を確認したので、備忘録として残しています。
ItemTouchHelper を使用して RecyclerView のアイテムをドラッグ&ドロップで並べ替え、スワイプで削除する方法についても確認しました。
試作品は以下のような感じです↓↓

RecyclerView で表示するアイテムには、複数のレイアウトを指定できます。
上記試作品でも、3種類のレイアウトファイルを使用しています。
- TextView が2つあるレイアウト(ViewType1 の下にあるアイテム)
- TextView が1つだけのレイアウト(ViewType2 の下にあるアイテム)
- アイテムを区切るセクションタイトルのレイアウト(「ViewType1 」「ViewType2」 というテキストを表示しているアイテム)
上記試作品のソースコードは、GitHub にアップしています。
RecyclerView の基本
RecyclerView を使ってデータセットをリスト表示するためには、以下のクラスを実装します。
| RecyclerView.Adapter |
|
| RecyclerView.ViewHolder |
|
| RecyclerView.LayoutManager |
|
- Adapter によって RecyclerView に表示するデータを定義する
- RecyclerView の1行ごとの参照は、Adapter -> ViewHolder に保持されている
- RecyclerView のレイアウト(水平・垂直・グリッド表示など)は、LayoutManager によって管理される
レイアウトファイルの作成
RecyclerView を配置
RecyclerView のアイテム用のレイアウトを作成
試作品では3つのレイアウトファイルを作成しています。以下は1例になります。
Adapter の実装
RecyclerView に表示するデータとViewを結びつける Adapter を実装します。
アイテム行への参照を保持する ViewHolder も、Adapter の中で定義しています。
class SampleAdapter (var myDataset : ArrayList, var activity: MainActivity) : RecyclerView.Adapter (){ /* ViewHolder の実装 */ open class SampleViewHolder(v : View) : RecyclerView.ViewHolder(v) { var titleTextView : TextView = v.findViewById(R.id.title_text_view) } open class DefaultSampleViewHolder(v : View, activity: MainActivity) : SampleViewHolder(v) { var thumbnailImageView : ImageView = v.findViewById(R.id.thumbnail_image_view) var hamburgerImageView : ImageView = v.findViewById(R.id.hamburger_image_view) init { //省略... } } class SubSampleViewHolder(v : View, activity: MainActivity) : DefaultSampleViewHolder(v, activity){ var subTitleTextView : TextView = v.findViewById(R.id.subtitle_text_view) } // ViewType に応じてレイアウトを決定し、View(ViewHolder)を作成する。 override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): SampleViewHolder { //viewType に応じてレイアウトを変更している。 return when (viewType){ RecyclerViewCustomViewType.SECTION.ordinal -> { val v = LayoutInflater.from(viewGroup.context) .inflate(R.layout.sample_recyclerview_row_view_section, viewGroup, false) SampleViewHolder(v) } RecyclerViewCustomViewType.DEFAULT.ordinal -> { val v = LayoutInflater.from(viewGroup.context) .inflate(R.layout.sample_recyclerview_row_view_default, viewGroup, false) DefaultSampleViewHolder(v, activity) } else -> { val v = LayoutInflater.from(viewGroup.context) .inflate(R.layout.sample_recyclerview_row_view_sub, viewGroup, false) SubSampleViewHolder(v, activity) } } } //表示するView(ViewHolder)に、データを紐づける。 override fun onBindViewHolder(holder : SampleViewHolder, position: Int){ holder.titleTextView.text = myDataset[position].title if (holder is DefaultSampleViewHolder) holder.thumbnailImageView.setImageResource(myDataset[position].imageResource) if (holder is SubSampleViewHolder) holder.subTitleTextView.text = myDataset[position].subTitle } // データの数を返す override fun getItemCount(): Int = myDataset.size // アイテムのViewTypeを返す override fun getItemViewType(position: Int): Int { return when(myDataset[position].viewType){ RecyclerViewCustomViewType.SECTION -> RecyclerViewCustomViewType.SECTION.ordinal RecyclerViewCustomViewType.SUB -> RecyclerViewCustomViewType.SUB.ordinal else -> RecyclerViewCustomViewType.DEFAULT.ordinal } } }
RecycerView の設定をする
RecyclerView に Adapter を渡したり、LayoutManager を設定したりします。
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView : RecyclerView
private lateinit var viewAdapter : RecyclerView.Adapter<*>
private lateinit var viewManager : RecyclerView.LayoutManager
private lateinit var myDataSet : ArrayList
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// テストデータの作成
myDataSet = createDataSet()
// RecyclerView の設定
viewManager = LinearLayoutManager(this)
viewAdapter = SampleAdapter(myDataSet, this)
recyclerView = findViewById(R.id.recycler_view).apply{
setHasFixedSize(true)
layoutManager = viewManager
adapter = viewAdapter
}
ドラッグ & ドロップ、スワイプでリスト操作をする
RecyclerView でドラッグ&ドロップやスワイプ操作を行うには、ItemTouchHelper クラスを利用します。
|
ItemTouchHelper |
|
|
|
|
|
(今回はこちらを利用しました。) |
SimpleCallback を実装する
ItemTouchHelper のコンストラクタに渡す必要がある Callback を実装します。
private fun getRecyclerViewSimpleCallBack() =
// 引数で、上下のドラッグ、および左方向のスワイプを有効にしている。
object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT) {
// ドラッグしたときに呼ばれる
override fun onMove(p0: RecyclerView, p1: RecyclerView.ViewHolder, p2: RecyclerView.ViewHolder): Boolean {
// どこからどこへアイテムが移動するのか
val fromPosition = p1.adapterPosition
val toPosition = p2.adapterPosition
/*
* ドラッグ時、viewType が異なるアイテムを超えるときに、
* notifyItemMoved を呼び出すと、ドラッグ操作がキャンセルされてしまう。
* (ドラッグは同じviewTypeを持つアイテム間で行う必要がある模様)
*
* 同じ ViewType アイテムを超える時だけ notifyItemMoved を呼び出す。
* */
if (p1.itemViewType == p2.itemViewType) {
// Adapter の持つ実データセットを操作している
myDataSet.add(toPosition, myDataSet.removeAt(fromPosition))
// Adapter にアイテムが移動したことを通知
viewAdapter.notifyItemMoved(fromPosition, toPosition)
}
return true
}
// スワイプしたとき
override fun onSwiped(p0: RecyclerView.ViewHolder, p1: Int) {
p0.let{
// 実データセットからアイテムを削除
myDataSet.removeAt(p0.adapterPosition)
// adapter に通知
viewAdapter.notifyItemRemoved(p0.adapterPosition)
}
}
ItemTouchHelper の設定
先ほどのMainActivityに処理を追加します。
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView : RecyclerView
private lateinit var viewAdapter : RecyclerView.Adapter<*>
private lateinit var viewManager : RecyclerView.LayoutManager
private lateinit var myDataSet : ArrayList
// 追加
private lateinit var itemTouchHelper : ItemTouchHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/* 省略 */
// RecyclerView のドラッグ、スワイプ操作に関する設定
itemTouchHelper = ItemTouchHelper(getRecyclerViewSimpleCallBack())
itemTouchHelper.attachToRecyclerView(recyclerView)
}
おわりに
結構省略してソースコードをのせています。。。
全体像は GitHub の方で確認していただければと思います!!!









