🍥구현 기능
- 메인 화면 기능 요구사항
- RecyclerView 항목 롱 클릭 했을 때, 삭제 여부를 묻는 다이얼로그 표시하기
- 취소 선택 시, 돌아가기
- 삭제 선택 시, 해당 항목 삭제 후 RecyclerView에 반영하기
🍥구현하기
MyAdapter.kt
- RecyclerView 항목 롱 클릭 이벤트를 처리하기 위해
(1) ItemClick 인터페이스에 항목 롱 클릭을 처리하기 위한 함수 onLongClick 추가
(2) 항목에 ItemClick 인터페이스의 onLongClick() 메서드를 호출하는 OnLongClickListner 설정
(롱 클릭 이벤트 처리 후, 클릭 이벤트 처리를 원하지 않기 때문에, false 반환)
- 사용자가 특정 버튼을 클릭했을 때 이벤트 발생 순서:
Touch(down) -> Touch(up) -> OnClick
- 사용자가 특정 버튼을 롱 클릭했을 때 이벤트 발생 순서:
Touch(down) -> OnLongClick -> Touch(up) -> OnClick
- 이벤트 처리 후 true를 반환하면, 이벤트 처리가 종료되고
이벤트 처리 후 false를 반환하면, 다음 이벤트가 호출된다
- ⭐주의할 점: position이 아닌 adapterPosition 사용하기!
- 데이터 셋 변경 후 항목을 클릭했을 때,
실제 위치와 항목 클릭 이벤트에서 반환하는 position 값이 다른 문제 발생!
디테일 화면에서 클릭한 항목과 다른 항목의 정보가 표시되는 문제 발생!
- position 대신 adapterPosition을 사용하니, 문제 해결됨
문제가 발생하는 이유?
- notifyItemRemoved나 notifyItemInserted 등의 함수를 호출했을 때,
RecyclerView의 Adapter는 모든 항목을 reinitialize/redraw 하지 않는다
-> 삭제될 항목만 삭제하거나, 삽입될 항목만 삽입한다
-> 나머지 항목들은 데이터 셋이 변경되기 전의 position 값을 지닌다
해결 방법
- RecyclerView.Adapter 클래스의 getAdapterPosition() 메서드 사용
-> 항목의 현재 RecyclerView에서의 올바른 위치 반환
interface ItemClick{
fun onClick(view:View, position:Int)
fun onLongClick(view:View, position:Int)
}
class MyAdapter(private val dataSet: MutableList<Post>) : RecyclerView.Adapter<MyAdapter.MyHolder>() {
var itemClick:ItemClick? = null
override fun onBindViewHolder(holder: MyHolder, position: Int) {
holder.apply {
itemView.setOnClickListener {
itemClick?.onClick(it, adapterPosition)
}
itemView.setOnLongClickListener {
itemClick?.onLongClick(it, adapterPosition)
false
}
}
}
}
MainActivity.kt
- RecyclerView 항목 삭제 후,
데이터 셋에 변경사항이 발생했음을 RecyclerView의 Adapter에 알려야한다!
- 2 different data change events:
- item changes: single item changed, no positional changes
- structural changes: item insert/removed/moved within data set
item changes & structural changes
- 데이터 셋이 전체적으로 변경되었을 경우
- public final void notifyDataSetChanged()
item changes
- 아이템 내용 변경
- public final void notifyItemChanged(int position)
- public final void notifyItemRangeChanged(int positionStart, int itemCount)
structural changes
- 아이템 추가
- public final void notifyItemInserted(int position)
- public final void notifyItemRangeInserted(int positionStart, int itemCount)
- 아이템 삭제
- public final void notifyItemRemoved(int position)
- public final void notifyItemRangeRemoved(int positionStart, int itemCount)
- 아이템 이동
- public final void notifyItemMoved(int fromPosition, int toPosition)
private fun initRecyclerView(dataSet: MutableList<Post>) {
binding.mainRecyclerView.apply {
adapter = MyAdapter(dataSet).apply {
itemClick = object : ItemClick {
override fun onClick(view: View, position: Int) {
}
override fun onLongClick(view: View, position: Int) {
Log.d(TAG, "onLongClick) position: $position")
showDeleteDialog(position)
}
}
}
}
}
private fun showDeleteDialog(position: Int) {
val builder = AlertDialog.Builder(this).apply {
setTitle(R.string.delete_dialog_title)
setMessage(resources.getString(R.string.delete_dialog_msg, dataSet[position].title))
setIcon(R.drawable.icon_dialog)
}
val listener = DialogInterface.OnClickListener { _, p1 ->
when (p1) {
DialogInterface.BUTTON_POSITIVE -> deleteRecyclerViewItem(position)
DialogInterface.BUTTON_NEGATIVE -> {}
}
}
builder.setPositiveButton(R.string.dialog_positive, listener)
builder.setNegativeButton(R.string.dialog_negative, listener)
builder.show()
}
private fun deleteRecyclerViewItem(position: Int) {
dataSet.removeAt(position)
binding.mainRecyclerView.adapter?.notifyItemRemoved(position)
}