0101011001010111

1-3 RecyclerView 본문

Kotlin/안드로이드_[숙련]앱개발

1-3 RecyclerView

[진주] 2023. 8. 27. 06:15
728x90
반응형

앞서 배웠던 CustomView를 이용해서 RecyclerView 라는 걸 할건데, 

RecyclerView는 뭐냐면,

 

한정적인 화면에 많은 데이터를 넣을 수 있는 View입니다.

Recycle = 재활용하다 

 

즉 , View를 재활용해서 사용하겠다는 말이다.

 

기존의 ListView는 항목을 만들 때마다 새로운 아이템을 계속 뷰 밑으로 생성하게 된다.

 

이미지로 대충 느낌을 보자.

▼먼저, ListView는

  • 사용자가 스크롤 할 때마다 위에 있던 아이템은 삭제되고, 맨 아래의 아이템은 생성 되길 반복합니다
  • 아이템이 100개면 100이 삭제 생성됩니다. 즉 계속 삭제와 생성을 반복하므로 성능에 좋지않습니다.

 

▼그럼, RecyclerView는

  • 사용자가 스크롤 할 때, 위에 있던 아이템은 재활용 돼서 아래로 이동하여 재사용 합니다.
  • 즉 아이템이 100개여도 10개정도의 View만 만들고 10개를 재활용해서 사용합니다.
  • View를 계속 만드는 ListView의 단점을 보완하기 위해 나왔습니다.

 


 

RecyclerView사용하기

 

Adapter

  • Adapter란 데이터 테이블을 목록 형태로 보여주기 위해 사용되는 것으로, 데이터를 다양한 형식의 리스트 형식을 보여주기 위해서 데이터와 RecyclerView 사이에 존재하는 객체이다.
  • 즉 데이터와 RecyclerView 사이의 통신을 위한 연결체이다.

▲기존 Adapter와 동일하다.

 

 

 

 

 

▼다만, RecyclerView에서만 추가로 들어가는 부분이 있는데, 

ViewHolder

  • ViewHolder란 화면에 표시될 데이터나 아이템들을 저장하는 역할 입니다. //저장을 해야 뷰를 똑같이 복사해서 불러다 쓰지 
  • RecyclerView의 개념을 적용하기위해선 스크롤 해서 위로 올라간 View를 재활용하기 위해서 이 View를 기억하고 있어야 합니다. ViewHolder가 그역할을 합니다.

 

 

 


어떻게 만드는지 해보자! 

 

RecyclerView예제

  • RecyclerView폴더를 새로 만들고 이전에 실습한 CustomItemView복사한다.
  • AndroidStudioFile > Open을 눌러 RecyclerView폴더를 연다.
  • 메인화면 레이아웃(예,activity_main.xml)에 ListView위젯을 RecyclerView위젯으로 변경한다.

기존의 activity_main.xml 부분의 ListView를 RecyclerView로 변경! 

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

 <androidx.recyclerview.widget.RecyclerView
      android:id="@+id/listView"
      android:layout_width="match_parent"
      android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

ㄴ recyc 치다보면 자동완성으로 나옴 

 

 

 

item.xml의 이름을 헷갈리니까 itme_recyclerview.xml로 변경 

 

 

MyAdapter.kt

class MyAdapter(val mItems: MutableList<MyItem>) : RecyclerView.Adapter<MyAdapter.Holder>() {

    interface ItemClick {
        fun onClick(view : View, position : Int)
    }

    var itemClick : ItemClick? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return Holder(binding)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.itemView.setOnClickListener {  //클릭이벤트추가부분
            itemClick?.onClick(it, position)
        }
        holder.iconImageView.setImageResource(mItems[position].aIcon)
        holder.name.text = mItems[position].aName
        holder.age.text = mItems[position].aAge
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

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

    inner class Holder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) {
        val iconImageView = binding.iconItem
        val name = binding.textItem1
        val age = binding.textItem2
    }
}

 

여기서 내가 궁금한건 ItemRecyclerviewBinding 이 item_recyclerview.xml이던데

이름이 다른데 어떻게 이렇게 연결 되는지가 궁금했다.

 

 

 

▼답 : 

View Binding의 네이밍 규칙

Android의 View Binding은 XML 파일의 이름을 바탕으로 바인딩 클래스의 이름을 자동으로 생성합니다.

생성 규칙은 다음과 같습니다:

  1. XML 파일 이름을 카멜 케이스로 변경합니다.
  2. XML 파일 이름에 포함된 _ (언더바)를 제거합니다.
  3. XML 파일 이름의 첫 글자를 대문자로 변환합니다.
  4. 마지막에 "Binding"이라는 단어를 추가합니다.

예를 들어, XML 파일 이름이 item_recyclerview.xml일 때:

  1. _ (언더바)를 제거합니다. 그러면 itemrecyclerview가 됩니다.
  2. 첫 글자를 대문자로 바꿉니다. 그러면 Itemrecyclerview가 됩니다.
  3. 마지막에 "Binding"을 추가하면, 최종적으로 ItemRecyclerviewBinding이 됩니다.

따라서 item_recyclerview.xml 파일은 View Binding을 사용할 때 ItemRecyclerviewBinding이라는 이름의 클래스로 변환됩니다.

 

 

올.. 그렇군...

 


 

 

inner class Holder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) {
    val iconImageView = binding.iconItem
    val name = binding.textItem1
    val age = binding.textItem2

일단 홀더에서는 ,

 

val iconImageView = binding.iconItem

아이템 뷰는 binding한 iconItem 

 

val name = binding.textItem1
val age = binding.textItem2

이름, 나이 이렇게 해서 홀더를 하나 만들고 

 

 

 

 

그 홀더를, 

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {

 

onCreateViewHolder에서 리턴하게 되는거에요 . 

 

 

 

 

 

또, 리스트에서 한개의 항목을 불러올 때 마다.

override fun onBindViewHolder(holder: Holder, position: Int) {

 

onBindViewHolder가 생성이 됩니다. 

ㄴ 그럼 여기 파라미터에있는 

 

holder와 position을 받아서 

holder.itemView.setOnClickListener {  //클릭이벤트추가부분
    itemClick?.onClick(it, position)
}
holder.iconImageView.setImageResource(mItems[position].aIcon)
holder.name.text = mItems[position].aName
holder.age.text = mItems[position].aAge

해당 홀더에 아이콘, 사진을 넣고 이름과 나이를 집어넣게 돼요.

 

 

 

그래서 RecyclerView의 어탭터는 기존의 커스텀 뷰 어댑터랑 좀 다른 부분은 

inner class Holder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) {
    val iconImageView = binding.iconItem
    val name = binding.textItem1
    val age = binding.textItem2

* 먼저 홀더를 하나 만들어야 한다는거 

 

 

그리고, onCreateViewHolder에서 

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
    val binding = ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
    return Holder(binding)

return Holder를 해야하는거! 

 

 

 

 

(전에 ListView할때는 getView를 했었다. 하지만 RecyclerView에서는  onBindViewHolder로 이런 값을 넣는다.)

override fun onBindViewHolder(holder: Holder, position: Int) {
    holder.itemView.setOnClickListener {  //클릭이벤트추가부분
        itemClick?.onClick(it, position)
    }
    holder.iconImageView.setImageResource(mItems[position].aIcon)
    holder.name.text = mItems[position].aName
    holder.age.text = mItems[position].aAge
}

 

 

 


 

 

 

 

MainActivity

 

 

 

 

실행해보면 저번에 했던 커스텀뷰와 똑같이 나온다,

 

 

mainActivity.kt

package com.example.android_1_2_customviewrecyclerview

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.android_1_2_customviewrecyclerview.databinding.ActivityMainBinding


class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 데이터 원본 준비
        val dataList = mutableListOf<MyItem>()
        dataList.add(MyItem(R.drawable.jinju1, "Bella", "1"))
        dataList.add(MyItem(R.drawable.jinju2, "Charlie", "2"))
        dataList.add(MyItem(R.drawable.jinju3, "Daisy", "1.5"))
        dataList.add(MyItem(R.drawable.jinju4, "Duke", "1"))
        dataList.add(MyItem(R.drawable.jinju5, "Max", "2"))
        dataList.add(MyItem(R.drawable.jinju6, "Happy", "4"))
        dataList.add(MyItem(R.drawable.jinju7, "Luna", "3"))
        dataList.add(MyItem(R.drawable.jinju8, "Bob", "2"))
        
        val adapter = MyAdapter(dataList)
        binding.recyclerView.adapter = adapter
        binding.recyclerView.layoutManager = LinearLayoutManager(this)

        adapter.itemClick = object : MyAdapter.ItemClick {
            override fun onClick(view: View, position: Int) {
                val name: String = dataList[position].aName
                Toast.makeText(this@MainActivity," $name 선택!", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

 

한 번 더 설명을 하자면,

먼저 데이터 클래스가 사용할 아이템에 대한 레이아웃이 먼저 만들어 질 것이고,

 

 

그 다음 MyItem 이라는 data class를 만들면 되고, 

package com.example.android_1_2_customviewrecyclerview

data class MyItem(val aIcon:Int, val aName:String, val aAge:String) {}

 

 

 

 

그 다음 리사이클러뷰에 대한 어뎁터는 

class MyAdapter(val mItems: MutableList<MyItem>) : RecyclerView.Adapter<MyAdapter.Holder>() {

RecyclerView.Adapter를 상속받아서 아이템을 집어넣고,

 

 

 

 

그리고 이부분 설명이 빠졌는데 

var itemClick : ItemClick? = null

 

인터페이스를 하나 만들어야 하는데, 

리사이클러뷰는 인터페이스를 따로 만들어 줘야 합니다.

 

그래서 itemClick이라는 아이를 하나 만들고, 그 itemClick은

 

interface ItemClick {
    fun onClick(view : View, position : Int)
}

이 itemClick 인터페이스에 대한 얘를 만들어서 

인터페이스 안에는 onClick이 들어가는거구요 

 

그 온클릭은 

override fun onBindViewHolder(holder: Holder, position: Int) {
    holder.itemView.setOnClickListener {  //클릭이벤트추가부분
        itemClick?.onClick(it, position)

여기에 들어가게 된다.

 

 

아이템이 클릭되게 되면 어탭터와 이 MainActivity사이의 통신을 위해서 만들어준 거다.

 

 

 

adapter.itemClick = object : MyAdapter.ItemClick {
    override fun onClick(view: View, position: Int) {
        val name: String = dataList[position].aName
        Toast.makeText(this@MainActivity," $name 선택!", Toast.LENGTH_SHORT).show()

그 통신이 어떻게 되냐면 어탭터에서 아이템 클릭 됐을 때,  onClick으로 메서트 호출이 되는 거죠.

 

그래서 어댑터랑 MainActivity와 둘이 통신할 때는 이런식으로 인터페이스를 따로 뺀다는 거 기억하시고 나중에 활용 하시면 될 것 같아요.

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90
반응형

'Kotlin > 안드로이드_[숙련]앱개발' 카테고리의 다른 글

kotlin_Dialog  (0) 2023.09.14
1-4 프래그먼트Fragment  (0) 2023.08.28
1-3. CustomView  (0) 2023.08.25
1-2.AdapterView_GridView_버튼클릭시 이벤트!  (0) 2023.08.25
1-2.AdapterView_GridView_imageVersion  (0) 2023.08.25