あんまり使うことがなくて、使おうとする度にどうやるんだっけ?と調べるやつ
RecyclerViewはその内部でドラッグドロップで並べ替えをする機能を実装するのは非常に簡単です。
まずは、シンプルなリストを作ってみましょう
private class ItemViewHolder(val binding: ItemViewBinding) : ViewHolder(binding.root)
private class ItemAdapter(context: Context) :
ListAdapter<Int, ItemViewHolder>(object : DiffUtil.ItemCallback<Int>() {
override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean = oldItem == newItem
override fun areContentsTheSame(oldItem: Int, newItem: Int): Boolean =
oldItem == newItem
}) {
private val inflater = LayoutInflater.from(context)
private val list: MutableList<Int> = MutableList(10) { it + 1 }
init {
submitList(list.toList())
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder =
ItemViewHolder(ItemViewBinding.inflate(inflater, parent, false))
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.binding.text.text = "item: " + getItem(position).toString()
}
}
レイアウトとかもシンプルなものなので省略、これで以下のようなリストを作ることができました。
次に、上記Adapterに項目の入れ替え、削除を行うメソッドを追加します。Adapterに実装する必要はないですが、あくまで説明用のサンプルとしてです。
fun swap(from: Int, to: Int) {
list.add(to, list.removeAt(from))
submitList(list.toList())
}
fun remove(target: Int) {
list.removeAt(target)
submitList(list.toList())
}
LinearLayoutManagerで縦一列に並べている場合、通常onMoveは前後に動く度にコールされるので単純にfrom/toでswapしても同じですが、単純にfrom/toを入れ替えるのではなく、fromからtoの間を順次移動させてtoにfromの項目を入れる用にしています。
上記メソッドを呼び出すようにItemTouchHelperを実装します。
val helper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.START or ItemTouchHelper.END
) {
override fun onMove(recyclerView: RecyclerView, viewHolder: ViewHolder, target: ViewHolder): Boolean {
adapter.swap(viewHolder.adapterPosition, target.adapterPosition)
return true
}
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
adapter.remove(viewHolder.adapterPosition)
}
})
helper.attachToRecyclerView(binding.recyclerView)
第一引数がドラッグの方向で、第二引数がスワイプ方向ですね。
ドラッグ(onMove)が発生したときはtargetと位置を入れ替える操作を、スワイプ(onSwipe)が発生したときは削除の動作をそれぞれつないでいるわけです。
こうすると、以下のようにドラッグドロップで並べ替え、スワイプで削除が実装できます
drag&drop | swipe |
---|---|
超簡単です。
GridLayoutManager
グリッドレイアウトの場合、ドラッグ方向は上下左右になりますね。
val helper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN or
ItemTouchHelper.START or ItemTouchHelper.END, 0
) {
override fun onMove(recyclerView: RecyclerView, viewHolder: ViewHolder, target: ViewHolder): Boolean {
adapter.swap(viewHolder.adapterPosition, target.adapterPosition)
return true
}
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
adapter.remove(viewHolder.adapterPosition)
}
})
helper.attachToRecyclerView(binding.recyclerView)
また、並べ替えのロジックによって振る舞いが結構違います。
前項目と同様に、from/toの間を詰めて移動させる場合
fun swap(from: Int, to: Int) {
list.add(to, list.removeAt(from))
submitList(list.toList())
}
from/toを単純に入れ替える場合
fun swap(from: Int, to: Int) {
Collections.swap(list, from, to)
submitList(list.toList())
}
それぞれ、以下のような動作になります。どちらがいいか、また別のロジックが良いかはアプリの特性によって決めれば良いと思います。
1 | 2 |
---|---|
以上です。