마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

리사이클러뷰의 어댑터 클래스 내부에서 메인 액티비티로 데이터 전달.

0 추천

프래그먼트를 상속하는 액티비티에, 리사이클러뷰의 어댑터 하나를 선언해놓았습니다.

 

이 어댑터 클래스 내부에서, 메인 액티비티로 데이터만 전달하려고 합니다.

(프래그먼트 전환이나 액티비티 전환 없이, 데이터만 전달하는 코드입니다.)

 

그런데, 어댑터 클래스 내부에서는 val activity = MainActivity() 이 객체가 없으면,

 

(acitivity ....<- 이 acitivity 부분이 빨간 글씨가 됩니다. ↓

(activity as MainActivity?)?.setDataAtFragment3("1")

 

위 코드는 프래그먼트 전환 없이, 데이터만 주고 받으려고 작성한 코드입니다. 어댑터 밖에서는 잘 작동합니다. 어댑터 내부에서만 작동을 하지 않습니다.

 

리사이클러뷰를 롱 클릭 했을때, 밑에서 바텀네비게이션뷰가 올라오도록 애니메이션을 설정해둔 상태입니다.

 

그런데, 리사이클러뷰를 롱 클릭하면, 화면이 강제로 종료되면서, 어플이 종료되고, 스마트폰 배경화면으로 나가버려집니다.

 

무엇때문에 종료되는 것인지 잘 모르겠으나, 아마도 프래그먼트에서 액티비티로 데이터 전달하는 방식이

 

잘못되어서 그런것 같습니다. 새로 추가한 저 객체 때문에 오류가 나는것 같습니다.

 

activity 부분에 무엇을 넣어주어야 정상 작동 할는지 잘 모르겠습니다.

private val tandf2 = SparseBooleanArray(0)
        var istrue: Boolean = false     //이 코드는 롱 클릭 후, 숏 클릭을 가능하게 해주려고 만든 코드다.
        var vpos: ArrayList<Int>? = null
        // 순서 7번 : 이렇게 만들어진 view객체를 인수로 사용하여 ViewHolder클래스로 반환해주면,
        // ViewHolder클래스에서 메모리에 저장했다가, onBindViewHoler() 호출시 onBindViewHoler()의 매개변수에 전달하는 구조다.
        // 이 뷰홀더 클래스에서 저장하고 있는 것들은, 뷰에 들어갈 icon, title, desc가 아니라, 뷰 그 자체다. 즉, 껍데기를 저장했다가, 바인딩 뷰홀더로 껍데기만 보내주는 것이다.
        inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            //var icon: ImageView
            var title: TextView
            var desc: TextView
            var activity = MainActivity()

            init {
                // 뷰 객체에 대한 참조. (hold strong reference)
                //icon = itemView.findViewById(R.id.ry_icon)
                title = itemView.findViewById(R.id.ry_title)
                desc = itemView.findViewById(R.id.ry_desc)


                //이벤트 소스, 이벤트 리스너, 이벤트 핸들러
                itemView.setOnLongClickListener(object : View.OnLongClickListener { // 컨버터 쓰면 object는 표기해주지 않는데, 이것으로 인해서 오류가 생기고, 도움말도 이것을 지적해주지 않아. 이거 조심하자.
                    override fun onLongClick(v: View): Boolean {
                        val pos = adapterPosition      // 뷰홀더 클래스에서만 제공하는 아이템들의 포지션을 구해주는 함수다.
                        if (pos != RecyclerView.NO_POSITION) {      // 아이템 뷰 하나가 재활용 될 때에, 또 다른 아이템 뷰 하나가 삭제되므로, 노 포지션을 반환하기 때문에 이렇게 조건문에 넣어줘야 한다고 한다.
                            if (istrue != true) {
                                (activity as MainActivity?)?.setDataAtFragment3("1")
                                tandf2.put(pos, true)
                                v.setBackgroundColor(0x4d00FF7B)
                                istrue = true
                                /*
                                when (pos) {
                                    pos -> vpos = arrayListOf(pos)       // 여기서 포지션값을 변수에 저장해놓으면, 그 포지션값을 다시 가져와서,
                                }                           // 선택된 포지션은 다시 화이트로, 선택되지 않은 포지션들은 회색으로 칠해 줄 수 있도록 조건문을 작성한다.
                                */
                            } else {
                                (activity as MainActivity?)?.setDataAtFragment3("0")
                                tandf2.clear()      //이 코딩을 해줘야, 롱클릭을 다시 눌렀을때, 전체 해제된다.
                                notifyDataSetChanged()
                                istrue = false
                            }

                            // 데이터 리스트로부터 아이템 데이터 참조. - 화석
                            /*
                        mData?.set(pos, RecyclerItem())
                        notifyItemChanged(pos)
                        */
                        }
                        return true     //true로 하면 진동이 느껴지고, false로 하면 무진동이 된다.
                    }
                })

                itemView.setOnClickListener(object : View.OnClickListener {
                    override fun onClick(v: View?) {
                        val pos = adapterPosition
                        if (pos != RecyclerView.NO_POSITION) {
                            if (istrue == true) {
                                if (tandf2.get(pos, false)) {

                                    tandf2.put(pos, false)
                                    v?.setBackgroundColor(0x000000)

                                } else {

                                    tandf2.put(pos, true)
                                    v?.setBackgroundColor(0x4d00FF7B/*Color.BLUE*/)

                                }
                            }
                            /*
                            when (pos) {
                                pos -> vpos = arrayListOf(pos)
                            }
                            when (vpos) {
                                vpos -> {
                                    desc.setBackgroundColor(0xffffff)
                                }
                            }*/
                        }
                    }
                })
            }
        }

 

상쾌한 (1,890 포인트) 님이 2021년 1월 25일 질문

1개의 답변

+1 추천
 
채택된 답변

Adapter안에는 Activity에 인스턴스에 대한 참조를 가지고 있으면 안됩니다. 라이프사이클 때문에 Activity가 존재하지 않게 되면 앱이 크래쉬될 수 있습니다.

대신 콜백을 사용하세요. ViewHolder에서 어떤 아이템이 눌렸는지만 Listener로 전달을 하시구요. 실제 데이터구조에 눌렸는지에 대한 flag 를 변경한 다음 Adapter 를 갱신하는 것은 Listener쪽의 책임입니다. ViewHolder는 뭐가 눌렸는지만 전달해 주면 되고, 눌린 데이터 값이 왔을 때 눌릴 상태를 그려주는 것 외에는 신경쓰지 말아야 합니다.

그리고 Item마다 눌린 상태를 가져갈 수도 있고, 눌린 상태만 저장할 수 있는 Array나 List를 사용하셔도 됩니다. BindViewHolder에서 ViewHolder.bindItem()에 어떤 아이템이 눌렸는지 전달해 줄 수 있으면 됩니다.

그리고 ViewHolder에 눌린 상태를 저장하면 안되는 이유가 ViewHolder는 재사용이 되기 때문에 재사용되는 아이템에 대한 상태값은 저장하게 됩니다. 따라서 상태값은 ViewHolder에 저장하시면, 나의 상태가 아닌 다른 아이템의 상태가 표시될 수도 있습니다.

        inner class ViewHolder(itemView: View,  private val listener: ItemClickListener) : RecyclerView.ViewHolder(itemView) {
            var title: TextView = findViewById(R.id.ry_title)
            var desc: TextView = findViewById(R.id.ry_desc)
           
 
            init {
                itemView.setOnLongClickListener(object : View.OnLongClickListener { 
                    override fun onLongClick(v: View): Boolean {
                        val pos = adapterPosition   
                        if (pos != RecyclerView.NO_POSITION) { 
                               listener.onItemClicked(pos);
                               return false;
                        }       
                        return true;
                    } 
                })
 
                itemView.setOnClickListener(object : View.OnClickListener {
                    override fun onClick(v: View?) {
                        val pos = adapterPosition
                        if (pos != RecyclerView.NO_POSITION) {
                                listener.onItemClicked(pos);   
                        }
                })
            }

           fun bindItem(item: YourType) {
                 //여기서에서 변경된 부분을 그려줌
           }


            private fun <T: View> findViewById(@IdRes id: Int): T = itemView.findViewById(id)

        }


fun interface ItemClickListener {
      fun onItemClicked(position: Int)
}


// Adapter
class Adapter : ItemClickListener {
    
     private var previousPosition =-1
     private var currentPosition = =1;

    override fun onItemClicked(position: Int) {
         items.getOrNull(previousPostion)?.selected = false;
         items.getOrNull(position)?.selected = true;

         notifyItemChanged(previousPosition);
         notifyItemChanged(position);

         previousPosition = currentPosition;
         currentPosition = position;        
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType:Int): ViewHolder {
          val itemView = ...
          ViewHolder(itemView, this@Adapter)
    } 

    override fun onBindViewHolder(vh: ViewHolder, position: Int)  {
          vh.bindItem(getItem[position])
    }
}

 

spark (226,420 포인트) 님이 2021년 1월 25일 답변
상쾌한님이 2021년 4월 11일 채택됨
감사합니다. ^^ 잘 읽었습니다.
...