Android/UI

[Android] RecyclerView Drag & Drop

찌김이 2022. 7. 28. 22:14
728x90
반응형

RecyclerView 에서 순서 정렬을 할 때 많이 쓰이는 Drag & Drop 기능에 대해서 간단하게 포스팅 하겠습니다.  

 

  • 저는 예제에 Databinding 을 사용했습니다.
// build.gradle (:app)
android {
   buildFeatures {
        dataBinding true
    }
 }

 

activity_main.xml

  • RecyclerView 하나 있는 예제 화면입니다.
<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    >
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

item_profile.xml

  • RecyclerView 에 들어갈 UI 입니다. 
  • ImageView 에 들어갈 파일은 Vector Asset 으로 추가합니다.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cardElevation="5dp"
        app:cardCornerRadius="26dp"
        android:layout_margin="8dp"
        >

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingVertical="10dp"
            android:paddingHorizontal="20dp">

            <ImageView
                android:id="@+id/ivProfile"
                android:layout_width="80dp"
                android:layout_height="80dp"
                android:scaleType="centerCrop"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                android:src="@drawable/ic_profile"
                />

            <TextView
                android:id="@+id/tvName"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                app:layout_constraintStart_toEndOf="@id/ivProfile"
                app:layout_constraintEnd_toStartOf="@id/ivHamburger"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                android:layout_marginHorizontal="20dp"
                android:textSize="20dp"
                android:textColor="@color/black"
                tools:text="닉네임"
                />

            <ImageView
                android:id="@+id/ivHamburger"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintStart_toEndOf="@+id/tvName"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                android:src="@drawable/ic_hamburger"
                />

        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.cardview.widget.CardView>
</layout>

ic_hamburger.xml
0.00MB
ic_profile.xml
0.00MB

 

ProfileAdapter.kt

  • RecyclerView 에 사용될 어댑터 입니다.
  • moveItem() 와 startDrag 로 Drag & Drop 을 동작합니다.
  • LongPress 로만 Drag & Drop 을 사용하시려면 startDrag 는 필요없습니다.
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.dragdrop.databinding.ItemProfileBinding
import java.util.*

class ProfileAdapter(
    private val startDrag : (holder : RecyclerView.ViewHolder) -> Unit
) : RecyclerView.Adapter<ProfileAdapter.ViewHolder>() {

    private val items = mutableListOf<String>()

    fun setItems(profiles : List<String>) {
        items.addAll(profiles)
    }
	
    fun moveItem(from: Int, to: Int) {
        Collections.swap(items, from, to)
        notifyItemMoved(from, to)
    }
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflatedView = LayoutInflater.from(parent.context)
        val binding = ItemProfileBinding.inflate(inflatedView, parent, false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = items[position]
        holder.bind(item)
    }
	
    override fun getItemCount() = items.count()
    
    inner class ViewHolder(
        private val binding: ItemProfileBinding
    ) : RecyclerView.ViewHolder(binding.root) {

        fun bind(item : String) {
            binding.apply {
                tvName.text = item

                ivHamburger.setOnTouchListener { _, _ ->
                    startDrag(this@ViewHolder)
                    false
                }

            }
        }
    }
}

 

MainActivity.kt

  • Drag & Drop 을 사용하기 위해 itemTouchHelper 를 구현합니다.
  • 햄버거 버튼을 누르면 onSelectedChanged 가 호출되고 위 아래로 움직일 때 onMove 가 호출됩니다.
  • onMove 에서 구현했던 아이템 변경 처리를 해주고 아이템을 놓는다면 onClearView 가 호출됩니다.
  • 이렇게 구현한 itemTouchHelper 를 attachToRecyclerView 를 통해 RecyclerView 에 연결해주면 구현이 끝납니다.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.example.dragdrop.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var profileAdapter: ProfileAdapter

    private val profiles = listOf(
        "프로필_1",
        "프로필_2",
        "프로필_3",
        "프로필_4",
        "프로필_5",
    )

    private val itemTouchHelper by lazy {
        val simpleItemTouchCallback = object : ItemTouchHelper.SimpleCallback(
            ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.START or ItemTouchHelper.END,
            0
        ) {

            override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder
            ): Boolean {
                val adapter = recyclerView.adapter as ProfileAdapter
                val from = viewHolder.adapterPosition
                val to = target.adapterPosition
                adapter.moveItem(from, to)

                return true
            }

            override fun isLongPressDragEnabled() = false

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

            override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
                super.onSelectedChanged(viewHolder, actionState)

                if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
                    viewHolder?.itemView?.alpha = 0.5f
                }
            }

            override fun clearView(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder
            ) {
                super.clearView(recyclerView, viewHolder)

                viewHolder.itemView.alpha = 1.0f
            }
        }

        ItemTouchHelper(simpleItemTouchCallback)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        initView()

    }

    private fun initView() {
        binding.apply {
            profileAdapter = ProfileAdapter {
                itemTouchHelper.startDrag(it)
            }
            itemTouchHelper.attachToRecyclerView(recyclerView)
            recyclerView.adapter = profileAdapter
            profileAdapter.setItems(profiles)
        }
    }
}

 

728x90
반응형

'Android > UI' 카테고리의 다른 글

[Android] BottomSheetDialog 만들기  (0) 2019.11.05