[Android 앱 개발 숙련] 과제 - 사과마켓 (5)메인 화면 기능 구현2

0
post-thumbnail

🍥구현 기능

  • 메인 화면 기능 요구사항
    • 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을 사용하니, 문제 해결됨

📌참고자료: Better Way to Get The Item Position in Android’s RecyclerView

문제가 발생하는 이유?

  • 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에 알려야한다!

📌참고자료: RecyclerView.Adapter | Android Developers

  • 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 {
            //...
            //set adapter
            adapter = MyAdapter(dataSet).apply {
                //set adapter itemClick
                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 -> {} //아무 동작 X
            }
        }
        builder.setPositiveButton(R.string.dialog_positive, listener)
        builder.setNegativeButton(R.string.dialog_negative, listener)
        builder.show()
    }

    private fun deleteRecyclerViewItem(position: Int) {
        //선택한 항목 삭제
        dataSet.removeAt(position)
        //항목 삭제 observer에 알리기
        binding.mainRecyclerView.adapter?.notifyItemRemoved(position)
    }
profile
Be able to be vulnerable, in search of truth

0개의 댓글