RecyclerView에서 Drag and Drop을 사용하려면 ItemTouchHelper를 사용해야 한다.
ItemTouchHelper는 RecyclerView에 Drag and Drop과 Swipe를 지원하는 유틸리티 클래스다.
ItemTouchHelper 객체를 RecyclerView에 연결하고 interface에 정의한 drag 관련 메서드들을 override하여 Drag and Drop을 할 수 있다.
Interface 정의
// Adapter에서 사용할 interface
interface ItemMoveListener {
// Drag 처리를 위한 메서드
fun onItemMove(fromPosition: Int, toPosition: Int): Boolean
// Drop 처리를 위한 메서드
fun onDropAdapter()
}
// Activity에서 사용할 interface
interface ItemStartDragListener {
// Drop이 됐을 때 Activity에서 사용할 메서드
fun onDropActivity(initList : ArrayList<SampleData>, changeList: ArrayList<SampleData>)
}
Drag를 시도할 때 Adapter에서 어떤 아이템이 이동됐는지 알아야하기 때문에 onItemMove에 fromPosition과 toPosition을 넣었다.
ItemStartDragListener에서는 초기 리스트와 변화된 리스트를 비교하려고 인자로 넣었다.
ItemTouchHelper.Callback()을 반환하는 클래스 작성
/**
* ItemTouchHelper는 RecyclerView에 Swipe와 Drag and Drop을 지원하는 유틸리티 클래스
*/
class RecyclerViewItemTouchHelperCallback(private val moveListener: ItemMoveListener) : ItemTouchHelper.Callback() {
/**
* 동작방식 구현(동작방향)
*/
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
return makeMovementFlags(dragFlags, 0)
}
/**
* Item이 위 아래로 움직일 때의 동작 구현
* - ItemTouchHelper가 드래그된 항목을 이전 위치에서 새 위치로 이동하려고 할 때 호출
*/
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
moveListener.onItemMove(viewHolder.adapterPosition, target.adapterPosition) // Adapter에 전달
return true
}
/**
* ItemTouchHelper로 Swipe 또는 Drag and Drop하여 ViewHolder가 변경될 때 호출
*/
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
when(actionState){
// 드래그 또는 스와이프가 끝났을 때 ACTION_STATE_IDLE가 전달 됨.
ItemTouchHelper.ACTION_STATE_IDLE -> moveListener.onDropAdapter() // Adapter에 전달
}
}
/**
* Item이 옆으로 움직일 때의 동작 구현
*/
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
TODO("Not yet implemented")
}
}
ItemTouchHelper.Callback()을 반환하는 클래스에서 getMovementFlags, onMove, onSwiped, onSelectedChanged를 재정의한다.
getMovementFlags는 Drag and Drop이나 Swipe를 사용할 것인가에 대한 동작방식을 선언하는 곳이다.
Drag and Drop만 사용할 것이기 때문에 Drag 플래그를(ItemTouchHelper.UP or ItemTouchHelper.DOWN) 반환해준다.
onMove는 Item이 움직이는게 감지되면 호출되는데 여기서 이동한 포지션을 알 수 있다.
Interface에 정의했던 onItemMove를 사용하여 Adapter에서 호출하도록 해야한다.
onSelectedChanged는 Drag and Drop을 하여 ViewHolder가 변경되었을 때 호출된다.
마찬가지로 Interface에 정의했던 onDropAdapter를 사용하여 Adapter에서 호출하도록 한다.
Adapter에서 ItemMoveListener 상속받기
class MainListAdapter :
RecyclerView.Adapter<MainListAdapter.ViewHolder>(),
ItemMoveListener {
private var mSampleList: ArrayList<SampleData> = ArrayList()
private var onItemDragListener: ItemStartDragListener? = null
var initList: ArrayList<SampleData> = ArrayList()
/**
* Activity에서 호출할 메서드
*/
fun itemDragListener(startDrag: ItemStartDragListener) {
this.onItemDragListener = startDrag
}
/**
* Drag and Drop하여 ViewHolder가 변경될 때 호출
*/
override fun onDropAdapter() {
onItemDragListener?.onDropActivity(initList, mSampleList) // Activity에 전달
}
/**
* Item이 바뀌면 리스트에 적용
*/
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
Collections.swap(mSampleList, fromPosition, toPosition)
notifyItemMoved(fromPosition, toPosition)
return true
}
/**
* 생략
*/
}
onItemMove로 Item이 움직이는게 감지되면 Collections.swap을 통해 리스트를 바꿔준다.
onDrapAdapter로 ViewHolder가 변경된 것을 확인하여 interface에 정의했던 onDropActivity를 사용하여 Activity에서 호출하도록 한다.
MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mBinding.root)
mAdapter = MainListAdapter()
val mCallback = RecyclerViewItemTouchHelperCallback(mAdapter)
mItemTouchHelper = ItemTouchHelper(mCallback)
mItemTouchHelper?.attachToRecyclerView(mBinding.rvMain) // ItemTouchHelper를 RecyclerView에 연결
mAdapter.setData(sampleList)
mBinding.rvMain.adapter = mAdapter
mBinding.rvMain.layoutManager = LinearLayoutManager(this)
mAdapter.itemDragListener(object : ItemStartDragListener{
override fun onDropActivity(
initList: ArrayList<SampleData>,
changeList: ArrayList<SampleData>
) {
// TODO : 드랍됐을 때 처리
println(initList) // 최초 리스트
println(changeList) // Drag and Drop 이후 리스트
println("------ \n")
}
})
}
attachToRecyclerView를 통해서 ItemTouchHelper 객체를 RecyclerView에 연결시키고,
onDropActivity를 override하여 Drop 됐을 때의 결과를 처리할 수 있다.
'Android > RecyclerView' 카테고리의 다른 글
[Android] RecyclerView에서 페이징+삭제 처리하기 (with Room) #2 (0) | 2023.01.09 |
---|---|
[Android] RecyclerView에서 페이징 처리하기 #1 (0) | 2022.12.16 |
[Android] Expandable RecyclerView 구현하기 (0) | 2022.11.22 |
[Android] RecyclerView 클릭 이벤트 처리하기 (0) | 2022.11.02 |
댓글