네이버 API로 가져온 뉴스들이다.
뉴스를 화면에 뿌려주는 것까지는 좋았는데 뉴스를 클릭할 시에 각각 뉴스의 링크로 이동하고 싶은데 어떻게 해야할까?
어뎁터 함수내에서 외부와 연결해 사용할 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(""","\"")
titleTv.setText(text)
text = news.description.replace(re, "").replace(""","\"")
contentTv.setText(text)
text = news.pubDate.replace(re, "").replace(""","\"")
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(""","\"")
titleTv.setText(text)
text = news.description.replace(re, "").replace(""","\"")
contentTv.setText(text)
text = news.pubDate.replace(re, "").replace(""","\"")
pubdataTv.setText(text)
itemView.setOnClickListener {
listener?.onItemClick(itemView,news,position)
}
}
}
}
이전에 메모장 만들 때 봐놨던 소스
여기서는 어뎁터를 연결할 때 어뎁터의 매개변수로 바로 함수(람다식)을 넣어버린다.
변수를 함수처럼 쓸 수 있는 함수형 언어인 코틀린의 장점을 잘 활용한 소스인 것 같다.
고차 함수
고차 함수란 함수의 인자에 함수를 사용할 수 있고 함수의 반환 값에 함수를 사용할 수 있다고 했다.
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())
})