open class ItemTouchHelper : RecyclerView.ItemDecoration, RecyclerView.OnChildAttachStateChangeListener
ItemTouchHelper.Callback.onChildDraw()
or ItemTouchHelper.Callback.onChildDrawOver()
를 사용하면 새로운 움직임을 정의하여 사용할 수 있다.attachToRecyclerView()
를 통해 RecyclerView 에 ItemTouchHelper를 attach
한다.private lateinit var recyclerView: RecyclerView
private lateinit var recyclerViewItemTouchHelper: RecyclerViewItemTouchHelper
private lateinit var itemTouchHelper: ItemTouchHelper
// ItemTouchHelper를 상속받아 정의한 class 객체 생성
recyclerViewItemTouchHelper = RecyclerViewItemTouchHelper(adapter)
// ItemTouchHelper에 직접 정의한 ItemTouchHelper 등록
itemTouchHelper = ItemTouchHelper(recyclerViewItemTouchHelper)
// RecyclerView에 직접 정의한 ItemTouchHelper attach!
itemTouchHelper.attachToRecyclerView(recyclerView)
attach
되는 것이므로, RecyclerView가 detached
된다면, ItemTouchHelper는 clearView()
를 호출한다.ACTION_STATE_IDLE : 정지 상태 (아무런 action이 취해지지 않았을 때)
ACTION_STATE_SWIPE : 뷰가 현재 스와이프되어지고 있는 상태
ACTION_STATE_DRAG : 뷰가 현재 드래그되어지고 있는 상태
ItemTouchHelper.UP
ItemTouchHelper.DOWN
ItemTouchHelper.LEFT
ItemTouchHelper.RIGHT
하는 역할은 같은데 방향을 정의하는 방식에 차이가 있는 것 같다.
Callback 은 개발자가 직접 getMovementFlags()
를 통해 방향을 설정하는데 SimpleCallback 은 생성자
를 통해 방향을 설정한다.
ItemTouchHelper.Callback
ItemTouchHelper.SimpleCallback
class RecyclerViewItemTouchHelper() : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN, // drag 방향
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT // swipe 방향
)
public abstract int getMovementFlags (
RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder
)
makeMovementFlags()
or makeFlag()
를 통해 편하게 flag를 생성할 수 있다.makeFlag() : 현재의 actionState 에서 어느 direction으로 움직일 것인지 정의하는 메소드
public static int makeFlag (int actionState, int directions)
위/아래
로 움직이고 싶을 때makeFlag(ItemTouchHelper.ACTION_STATE_IDLE, UP | DOWN)
오른쪽/왼쪽
으로 움직이고 싶을 때makeFlag(ItemTouchHelper.ACTION_STATE_DRAG, LEFT | RIGHT)
makeMovementFlags() : 이동 flag를 만들 수 있는 편리한 메소드
public static int makeMovementFlags (int dragFlags, int swipeFlags)
makeFlag() 는 현재 상태일 때 이 방향으로 움직이기! 와 같이 현재의 상태
도 정의해주어야 했다면, makeMovementFlags() 는 이동할 방향만 설정해주면 된다.
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val drag = ItemTouchHelper.UP or ItemTouchHelper.DOWN
val swipe = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
return makeMovementFlags(drag, swipe)
}
개발자 입장에서는 makeFlag() 보다 간편하게 방향을 설정할 수 있지만, 사실 makeMovementFlags()의 내부에서는 makeFlag()를 통해 움직임의 방향을 설정한다.
public static int makeMovementFlags(int dragFlags, int swipeFlags) {
return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags)
| makeFlag(ACTION_STATE_SWIPE, swipeFlags)
| makeFlag(ACTION_STATE_DRAG, dragFlags);
}
public abstract boolean onMove (
RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target
)
true
-> 새로운 위치로 이동 성공false
-> 새로운 위치로 이동 실패public abstract void onSwiped (
RecyclerView.ViewHolder viewHolder,
int direction
)
Drag | Swipe |
---|---|
RecyclerViewItemTouchHelper : ItemTouchHelper.Callback 구현 class
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
class RecyclerViewItemTouchHelper(
private val itemTouchHelperListener: ItemTouchHelperListener
) : ItemTouchHelper.Callback() {
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val drag = ItemTouchHelper.UP or ItemTouchHelper.DOWN
val swipe = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
return makeMovementFlags(drag, swipe)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
itemTouchHelperListener.onItemDragged(viewHolder.adapterPosition, target.adapterPosition)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
itemTouchHelperListener.onItemSwiped(viewHolder.adapterPosition)
}
}
RecyclerViewItemTouchHelper2 : ItemTouchHelper.SimpleCallback 구현 class
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
class RecyclerViewItemTouchHelper2(
private val itemTouchHelperListener: ItemTouchHelperListener
) : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
itemTouchHelperListener.onItemDragged(viewHolder.adapterPosition, target.adapterPosition)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
itemTouchHelperListener.onItemSwiped(direction)
}
}
ItemTouchHelperListener : drag, swipe 이벤트 처리를 위한 인터페이스
interface ItemTouchHelperListener {
fun onItemDragged(from: Int, to: Int)
fun onItemSwiped(position: Int)
}
RecyclerViewActivity : RecyclerView 정의 + ItemTouchHelper attach 한 class
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.study_android.R
class RecyclerViewActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: RecyclerViewAdapter
private lateinit var recyclerViewItemTouchHelper: RecyclerViewItemTouchHelper
private lateinit var itemTouchHelper: ItemTouchHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recyclerview)
val recyclerViewList = arrayListOf<RecyclerViewData>()
for(i in 1..50) {
recyclerViewList.add(RecyclerViewData(i, "테스트 $i"))
}
recyclerView = findViewById(R.id.main_recyclerview)
adapter = RecyclerViewAdapter()
// ItemTouchHelper를 상속받아 정의한 class 객체 생성
recyclerViewItemTouchHelper = RecyclerViewItemTouchHelper(adapter)
// ItemTouchHelper에 직접 정의한 ItemTouchHelper 등록
itemTouchHelper = ItemTouchHelper(recyclerViewItemTouchHelper)
// RecyclerView에 직접 정의한 ItemTouchHelper attach!
itemTouchHelper.attachToRecyclerView(recyclerView)
adapter.setList(recyclerViewList)
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
}
}
RecyclerViewAdapter : RecyclerView의 Adapter 구현 class
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.study_android.R
import java.util.ArrayList
class RecyclerViewAdapter : RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewViewHolder>(), ItemTouchHelperListener {
private var recyclerViewList = arrayListOf<RecyclerViewData>()
class RecyclerViewViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val number: TextView
val text: TextView
init {
number = view.findViewById(R.id.recyclerview_number)
text = view.findViewById(R.id.recyclerview_text)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.activity_recyclerview_item, parent, false)
return RecyclerViewViewHolder(view)
}
override fun onBindViewHolder(holder: RecyclerViewViewHolder, position: Int) {
holder.number.text = recyclerViewList[position].number.toString()
holder.text.text = recyclerViewList[position].text
}
override fun getItemCount(): Int = recyclerViewList.size
fun setList(list: ArrayList<RecyclerViewData>) {
this.recyclerViewList = list
}
override fun onItemDragged(from: Int, to: Int) {
val oldData = recyclerViewList[from]
recyclerViewList.removeAt(from)
recyclerViewList.add(to, oldData)
notifyItemMoved(from, to)
}
override fun onItemSwiped(position: Int) {
recyclerViewList.removeAt(position)
notifyDataSetChanged()
}
}
RecyclerViewData : data class
data class RecyclerViewData(
val number: Int,
val text: String
)
Android Developer - ItemTouchHelper
Android Developer - ItemTouchHelper.Callback
changhee09님 velog - ItemTouchHelper