[Android/Kotlin] RecyclerView 각각 아이템마다 클릭 이벤트 설정

Falco·2022년 5월 8일
1

Android

목록 보기
7/55

GitHub Link

네이버 API로 가져온 뉴스들이다.
뉴스를 화면에 뿌려주는 것까지는 좋았는데 뉴스를 클릭할 시에 각각 뉴스의 링크로 이동하고 싶은데 어떻게 해야할까?

Adapter에 OnClickInterface 설정하여 외부에서 함수 구현하기

어뎁터 함수내에서 외부와 연결해 사용할 interface를 만들어 주기

	// 외부에서 연결해 사용할 클릭 인터페이스 생성
    private var listener : OnClickInterface? = null
    interface OnClickInterface{
        fun onItemClick(v:View, news: NaverNews, pos:Int)
    }

    fun setOnItemClickListener(listener : OnClickInterface){
        this.listener = listener
    }

ViewHolder에서 bind할 때 원하는 뷰, 아이템에 클릭리스너를 달아주기

inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
        private val titleTv = itemView.findViewById<TextView>(R.id.item_navernews_title_tv)
        private val contentTv = itemView.findViewById<TextView>(R.id.item_navernews_content_tv)
        private val pubdataTv = itemView.findViewById<TextView>(R.id.item_navernews_pubdate_tv)

        // HTMl 마크업 태그 없애는 정규 식
        val re = "<(/)?([a-zA-Z]*)(\\s[a-zA-Z]*=[^>]*)?(\\s)*(/)?>".toRegex()

        fun bind(news: NaverNews) {
            // 태그와 따음표 바꿔주기
            var text: String = news.title.replace(re, "").replace("&quot;","\"")
            titleTv.setText(text)
            text = news.description.replace(re, "").replace("&quot;","\"")
            contentTv.setText(text)
            text = news.pubDate.replace(re, "").replace("&quot;","\"")
            pubdataTv.setText(text)
			
            // 아이템뷰 전체를 다 클릭했을 때 클릭리스너가 작동되게
            itemView.setOnClickListener {
                listener?.onItemClick(itemView,news,position)
            }
        }
    }

Adpater을 선언한 Fragment or Activity에서 Interface을 override하여 구현

			// 아이템 클릭 리스너 연결
            navernewsRvAdapter.setOnItemClickListener(object : MainNaverNewsAdapter.OnClickInterface{
                override fun onItemClick(v: View, news: NaverNews, pos: Int) {
                	// 인터넷창 새로 열기
                    startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(news.link)))
                }
            })

네이버 뉴스 RecyclerView의 Adapter 소스

class MainNaverNewsAdapter(navernews : List<NaverNews>)
    : RecyclerView.Adapter<MainNaverNewsAdapter.ViewHolder>() {

    private var news: List<NaverNews> = navernews

    // 외부에서 연결해 사용할 클릭 인터페이스 생성
    private var listener : OnClickInterface? = null
    interface OnClickInterface{
        fun onItemClick(v:View, news: NaverNews, pos:Int)
    }

    fun setOnItemClickListener(listener : OnClickInterface){
        this.listener = listener
    }

    override fun onCreateViewHolder(parent: ViewGroup, i: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_naver_news, parent, false)
//        val binding = ItemNaverNewsBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return ViewHolder(view)
    }

    override fun getItemCount(): Int {
        return news.size
    }

    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
        viewHolder.bind(news[position])
    }

    inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
        private val titleTv = itemView.findViewById<TextView>(R.id.item_navernews_title_tv)
        private val contentTv = itemView.findViewById<TextView>(R.id.item_navernews_content_tv)
        private val pubdataTv = itemView.findViewById<TextView>(R.id.item_navernews_pubdate_tv)

        // HTMl 마크업 태그 없애는 정규 식
        val re = "<(/)?([a-zA-Z]*)(\\s[a-zA-Z]*=[^>]*)?(\\s)*(/)?>".toRegex()

        fun bind(news: NaverNews) {
            // 태그와 따음표 바꿔주기
            var text: String = news.title.replace(re, "").replace("&quot;","\"")
            titleTv.setText(text)
            text = news.description.replace(re, "").replace("&quot;","\"")
            contentTv.setText(text)
            text = news.pubDate.replace(re, "").replace("&quot;","\"")
            pubdataTv.setText(text)

            itemView.setOnClickListener {
                listener?.onItemClick(itemView,news,position)
            }
        }
    }
}

람다식을 활용한 아이템 클릭 구현

이전에 메모장 만들 때 봐놨던 소스

Github Link

여기서는 어뎁터를 연결할 때 어뎁터의 매개변수로 바로 함수(람다식)을 넣어버린다.
변수를 함수처럼 쓸 수 있는 함수형 언어인 코틀린의 장점을 잘 활용한 소스인 것 같다.

고차 함수
고차 함수란 함수의 인자에 함수를 사용할 수 있고 함수의 반환 값에 함수를 사용할 수 있다고 했다.

adapter = MemoAdapter({ memo ->
			// memo password가 존재하면 passwd 입력 Dialog 출력
            if (memo.ispw) {
                Log.d(TAG, "MainActivity MemoAdapter memoPW :" + memo.pw.toString())
                showPwDialog(memo)
            } else {
            	// 메모 수정
                startAddactivity(memo)
            }
        }, { memo ->
        	// 메모 지우는 함수
            deleteDialog(memo)
        })

메모 어뎁터 생성할때는 함수로 받아온 변수들을 Unit으로 return값 없게 반환

class MemoAdapter(val memoItemClick: (Memo) -> Unit, val memoItemDeleteClick: (Memo) -> Unit)
    : RecyclerView.Adapter<MemoAdapter.ViewHolder>() {
    ....
    }

뷰홀더에서 각각 인자로 넘어온 함수들을 연결해 준다.

inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
        private val titleTv = itemView.findViewById<TextView>(R.id.item_memo_title_tv)
        private val contentTv = itemView.findViewById<TextView>(R.id.item_memo_content_tv)
        private val deleteIv = itemView.findViewById<ImageView>(R.id.item_memo_delete_iv)
        private val lockIv = itemView.findViewById<ImageView>(R.id.item_memo_lock_iv)
        private val dateTv = itemView.findViewById<TextView>(R.id.item_memo_date_tv)
        fun bind(memo: Memo) {
            titleTv.text = memo.title
            if(memo.ispw){
                lockIv.visibility = View.VISIBLE
                contentTv.text = "비밀글 입니다."
            }else{
                lockIv.visibility = View.GONE
                contentTv.text = memo.content
            }
            itemView.setOnClickListener {
            	// Item Click됬을 때
                memoItemClick(memo)
            }
            deleteIv.setOnClickListener {
            	// deleteIv가 선택됬을 때
                memoItemDeleteClick(memo)
            }
            dateTv.text = memo.date
        }
    }

아직 코틀린에 익숙하지 않아서 그런가 전체적으로 난해한 소스이다.


이 때 adapter 내의 메모들은 ViewModel을 Observe하여 값이 추가되거나 삭제, 수정되면 adapter을 재설정하게 해놨음

화면을 회전하거나, OnStart, OnPause 등이 될 때 화면을 다시 그려줄 필요가 없다.

memoViewModel.getAll().observe(this, Observer<List<Memo>>{ memos->
            // updateUI
            mainViewModel.updateSearchText("")
            adapter.setMemos(memos,mainViewModel.ordering.value.toString().toBoolean())
        })
profile
강단있는 개발자가 되기위하여

0개의 댓글