개발/Android

[안드로이드] RecyclerView Drag & Drop 구현 (GridLayout)

Eun 2022. 2. 27. 17:11

그리드레이아웃을 가진 리사이클러뷰 드래그앤드롭을 구현하였는데, 까먹기 전에 정리하려고 한다.

 

필요한 준비물은 MyTouchHelperCallback 코틀린파일과 DragAndDropAdapter 구현해야한다. 

(당연히 RecyclerView는 다 구현이 되어있어야겠죠?)

 

 

MyTouchHelperCallback.kt

 

ItemTouchHelper를 사용하기 위해서 ItemTouchHelper.CallBack을 구현해야한다. 

import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView


class MyTouchHelperCallback(
    private val itemMoveListener: OnItemMoveListener
) : ItemTouchHelper.Callback() {

    interface OnItemMoveListener {
        fun onItemMove(fromPosition: Int, toPosition: Int)
        fun onItemSwiped(position: Int)
    }

    /**
     * 어느 방향으로 움직일지에 따라서 flag 받는것을 정의
     * 드래그는 위, 아래 액션이기 때문에 up, down 을 넘겨줌
     */
    override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ): Int {
    // GridLayout 형식일 경우
        val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
        val swipeFlags = 0
        return makeMovementFlags(dragFlags, swipeFlags)
    }

    /**
     * 어느 위치에서 어느 위치로 변경하는지
     */
    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        itemMoveListener.onItemMove(viewHolder.adapterPosition, target.adapterPosition)
        return true
    }

    /**
     * 좌우 스와이프
     */
    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        itemMoveListener.onItemSwiped(viewHolder.adapterPosition)
    }

	// 길게 눌렀을 때 반응하는 함수
    override fun isLongPressDragEnabled(): Boolean = false
}

 

DragAndDropAdapter.kt

import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.rc_4.R
import com.example.rc_4.ZZimData
import com.example.rc_4.databinding.RvZzimPageItemBinding
import java.util.*
import kotlin.collections.ArrayList

 class DragAndDropAdapter(private val list: ArrayList<ZZimData>) :
    RecyclerView.Adapter<DragAndDropAdapter.ViewHolder>(),
    MyTouchHelperCallback.OnItemMoveListener {

    private lateinit var dragListener: OnStartDragListener
    lateinit var binding: RvZzimPageItemBinding

    inner class ViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder(
        LayoutInflater.from(parent.context).inflate(R.layout.rv_zzim_page_item, parent, false)
    ) {
        val mall_name: TextView = itemView.findViewById(R.id.tv_mall_name)
        val product_name: TextView = itemView.findViewById(R.id.tv_product_name)
        val sale: TextView = itemView.findViewById(R.id.tv_sale)
        val price: TextView = itemView.findViewById(R.id.tv_price)
        val ivMenu: ImageView = itemView.findViewById(R.id.iv_proudct_img)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        binding = RvZzimPageItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(parent)
    }

//    inner class ViewHolder(val binding: RvZzimPageItemBinding) : RecyclerView.ViewHolder(binding.root)
//    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        list[position].let {
            with(holder) {
                mall_name.text = it.mall_name
                product_name.text = it.product_name
                price.text = it.price
                sale.text=it.sale
                ivMenu.setImageResource(it.img)
                ivMenu.setOnTouchListener { view, event ->
                    if (event.action == MotionEvent.ACTION_DOWN) {
                        dragListener.onStartDrag(this)
                    }
                    return@setOnTouchListener false
                }
            }
        }
    }

    override fun getItemCount(): Int {
        return list.size
    }

    interface OnStartDragListener {
        fun onStartDrag(viewHolder: RecyclerView.ViewHolder)
    }

    fun startDrag(listener: OnStartDragListener) {
        this.dragListener = listener
    }

    fun onItemMoved(fromPosition: Int, toPosition: Int) {
        Collections.swap(list, fromPosition, toPosition)
        notifyItemMoved(fromPosition, toPosition)
    }

    override fun onItemMove(fromPosition: Int, toPosition: Int) {
        Collections.swap(list, fromPosition, toPosition)
        notifyItemMoved(fromPosition, toPosition)
    }

    override fun onItemSwiped(position: Int) {
        list.removeAt(position)
        notifyItemRemoved(position)
    }

}

 

MainActivity.kt

 

만든 어댑터를 메인액티비티에 붙여주면 끝!

 val adapter = DragAndDropAdapter(dataList)
        val callback = MyTouchHelperCallback(adapter)
        val touchHelper = ItemTouchHelper(callback)
        touchHelper.attachToRecyclerView(binding.rvZzimPage)
        binding.rvZzimPage.adapter = adapter
        adapter.startDrag(object : DragAndDropAdapter.OnStartDragListener {
            override fun onStartDrag(viewHolder: RecyclerView.ViewHolder) {
                touchHelper.startDrag(viewHolder)
            }
        })

결과물