개발/Android

[RecyclerView] 스와이프, 드래그&드롭 효과를 가진 RecyclerView 만들기

Patrick0422 2022. 3. 26. 17:42

여러분은 드래그&드롭을 통해 아이템 간 위치를 바꾸거나 아이템을 스와이프 해 삭제하는 등의 액션을 구현하는 것을 보신 적이 있을 것입니다.

 

이러한 액션들을 구현하기 위해서는 ItemTouchHelper의 콜백 클래스를 구현하면 됩니다.

Callback 클래스와 SimpleCallback 클래스는 드래그나 스와이프를 인식할 방향을 어디서 설정하느냐의 차이입니다.

class SwipeHelperCallback(private val mAdapter: MyAdapter): ItemTouchHelper.Callback() {

    // 입력을 감지할 방향 설정, 설정하지 않으려면 0
    override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ): Int = makeMovementFlags(UP or DOWN, LEFT)
    ....

△ 일반 Callback 클래스를 상속받을 경우 getMovementFlags() 함수에서 방향 설정

class SimpleSwipeHelperCallback: ItemTouchHelper.SimpleCallback(UP or DOWN, LEFT or RIGHT) {
    ....

△ SimpleCallback 클래스를 상속받을 경우 생성자에서 방향 설정

 

예를 들어 (UP or DOWN, LEFT)를 전달할 경우 드래그 액션과 왼쪽 방향으로의 스와이프만 인식하게 됩니다.

 

 

구현

저는 SimpleCallback 클래스를 구현하고 매개변수로 RecyclerView의 어댑터를 넣어 주었습니다.

class SimpleSwipeHelperCallback(private val mAdapter: MyAdapter): 
    ItemTouchHelper.SimpleCallback(UP or DOWN, LEFT or RIGHT) {
    
    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        TODO("Not yet implemented")
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        TODO("Not yet implemented")
    }
}

필수적으로 구현해야 하는 함수는 onMove()와 onSwiped() 두 개입니다.

onMove()는 아이템을 드래그할 때, onSwiped()는 아이템을 스와이프 할 때 호출됩니다.

Drag&Drop

먼저 onMove()를 통해 아이템 간 순서 변경을 만들어 보겠습니다.

fun swapData(fromPos: Int, toPos: Int) {
    Collections.swap(itemList, fromPos, toPos)
    notifyItemMoved(fromPos, toPos)
}

RecyclerView의 어댑터에 두 아이템의 위치를 바꿔주는 swapData()라는 함수를 만들어 주었습니다.

notifyItemMoved() 함수는 ReyclerView.Adapter가 기본 제공하는 함수로 두 위치의 아이템이 바뀌었다는 것을 알립니다.

override fun onMove(
    recyclerView: RecyclerView,
    viewHolder: RecyclerView.ViewHolder,
    target: RecyclerView.ViewHolder
): Boolean {
    mAdapter.swapData(viewHolder.adapterPosition, target.adapterPosition)
    return true
}

그 이후 onMove()에서 두 아이템의 위치와 함께 호출해 주었습니다.

결과

 

Swipe

스와이프된 항목을 삭제하기 위해, 마찬가지로 RecyclerView의 어댑터에 특정 위치의 아이템을 삭제하는 함수를 만들어 주었습니다. 또, 되돌리기 기능도 만들면 좋겠다 싶어 아이템 추가 함수와 아이템 가져오기 함수도 만들었습니다.

fun insertDataAt(pos: Int, item: String) {
    itemList.add(pos, item)
    notifyItemInserted(pos)
}

fun dataAt(pos: Int) = itemList[pos]

fun removeDataAt(pos: Int) {
    itemList.removeAt(pos)
    notifyItemRemoved(pos)
}

삭제는 onSwiped()에서 삭제 함수를 불러주면 끝입니다.

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
    mAdapter.removeDataAt(viewHolder.layoutPosition)
}

이제 되돌리기 기능을 만들어 보겠습니다.

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
    val pos = viewHolder.layoutPosition
    val data = mAdapter.dataAt(pos)

    mAdapter.removeDataAt(viewHolder.layoutPosition)
    Snackbar.make(viewHolder.itemView, "Item Removed.", Snackbar.LENGTH_LONG).setAction("Undo") {
        mAdapter.insertDataAt(pos, data)
    }.show()
}

삭제할 아이템과 위치를 저장해 두었다가, Undo 버튼을 가진 Snackbar를 띄워 Undo 버튼을 누르면 되돌릴 수 있게 만들었습니다.

 

참고 링크

 

ItemTouchHelper  |  Android Developers

androidx.car.app.managers

developer.android.com

 

[안드로이드] recyclerview drag and drop/swipe 기능 만들기

유튜브하다가 이건 또 어케하는 거야 싶어져서 만들어 봤는데 생각보다 너무 오래 걸렸다~~~~ 시작하기 전에 다른 분들이 알려주신 이슈 해결 사항도 여기 적어보고자 합니다!! 1, 왼쪽으로 스와

min-wachya.tistory.com