달달한 스토리

728x90
반응형

자주자주 사용하는 것들은 정말 나 또한 자주자주 적고 있다.

 

그 예를 든 것이 뷰 페이저와 리사이클러뷰인데,

 

최근에 코틀린 버전으로 뷰 페이저를 썼고,

 

저 두 가지는 이미 자바로 배운 것을 정리했다.

 

지금은 처음으로 코틀린으로 리사이클러뷰 사용법을 적어보려고 한다.

 

언어가 바뀌어도 어느 정도 이해를 해야 하는데,

 

내가 자바를 사용할 때도, 그리 리사이클러뷰에 대해서 잘 이해하지 못하고 넘어간 것 같다.

 

그래도 지금은 조금 진전이 있는 상태에서 리사이클러뷰(코틀린 버전)를 정리해보려고 한다.

 

(홍드 로이드 님의 영상을 보고 참고했습니다.)

 

우선 라이브러리를 추가해주세요.

 

 

dependencies {
    implementation "androidx.recyclerview:recyclerview:1.1.0"
    // For control over item selection of both touch and mouse driven selection
    implementation "androidx.recyclerview:recyclerview-selection:1.1.0"
    }

 


사용법

 

우선 뷰 페이저도 마찬가지이겠지만,

 

리사이클러뷰를 사용하려면 세 가지가 필요하다.

 

뷰에 어떤 인자가 들어갈지 틀을 만들어주는 클래스 하나와

 

내가 리사이클(재활용)할 뷰들의 아이디를 참조해주는 뷰 홀더 클래스

 

그리고, 이 뷰 홀더를 position에 맞게 생성해주고,

 

또다시 그 포지션에 맞게 리사이클해주고,

 

아이템을 몇 개 생성할 건지, 클릭 시 액션을 설정해줄 수 있는 어뎁터

 

이 세가지만 알면 리사이클러뷰를 다 알았다고 해도 무방하다.

 

하지만 이렇게 말로 해서는 무슨 소리인지 모르는 것이 사실이다.

 

우선 내가 사용할 뷰 아이템을 만들어보자.!

 

내가 생성할 뷰의 모습을 만드는 것이라고 생각하면 된다.

 

layout을 누르고 new키를 눌러 list_item.xml을 생성한다.

 

<?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="wrap_content">


    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_marginStart="8dp"
            android:layout_marginLeft="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:srcCompat="@tools:sample/avatars" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="15dp"
            android:layout_marginLeft="15dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="269dp"
            android:layout_marginRight="269dp"
            android:text="백수열"
            android:textSize="16sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/imageView"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/jobView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="136dp"
            android:layout_marginRight="136dp"
            android:text="안드로이드 개발자"
            android:textSize="16sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.555" />

        <TextView
            android:id="@+id/ageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="15dp"
            android:layout_marginLeft="15dp"
            android:layout_marginTop="44dp"
            android:layout_marginEnd="269dp"
            android:layout_marginRight="269dp"
            android:text="26살"
            android:textSize="16sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toEndOf="@+id/imageView"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

 

 

대충 이런 모습의 뷰가 만들어질 것이다.

 

마무리에서는 이 뷰가 여러 개가 되면, 재사용(리사이클)을 할 것이다.

 

그다음에는 activity_main에서 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/re"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

이런 식으로 리사이클러뷰를 만들어주자.

 

이후에 이 곳에다가 내가 만든 아이템을 코드를 통해 보내줄 것이다.

 

우선 여기까지 하고, 다음으로 넘어가 보자.

우선은 저 뷰 아이템에 들어갈 데이터 틀을 만들기 위해서

 

클래스를 하나 만들 것이다.

 

클래스로 만드는 이유는

 

이따가 어뎁터에 데이터를 넘길 때, 리스트 형태로 넘기게 되는데,

 

리사이클을 하는 바인드 홀더 부분에서 손쉽게 데이트를 집어넣을 수 있기

 

용이하기 때문이다.

 

그러니 클래스로 한번 만들어 보자.

 

java에 오른쪽 클릭 후 new에서 코틀린 파일을 클릭 후

 

Profiles.kt를 만들어주자.

 

class Profiles (val profile : Int, val age : Int, val name : String, val job : String)

그리고 다음 같이 클래스(틀)를 만들어 주자.

 

나 같은 경우는 뷰에 들어갈 사진(Int)과 나이와 이름 직업을 자료형에 맞게 넣어주었다.

 

그리고 어뎁터를 만들어보자.

 

class ProfileAdapter(val profileList : ArrayList<Profiles>) : RecyclerView.Adapter<ProfileAdapter.CustomViewHolder>() {

    //뷰홀더가 처음 생성될때
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileAdapter.CustomViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
        return CustomViewHolder(view).apply { //클릭시 액션
            itemView.setOnClickListener { //여기서 itemview는 뷰홀더의 아이템들을 의미한다.
                val curPos : Int = adapterPosition //누른 뷰의 순서값
                val profile : Profiles = profileList.get(curPos) //객체형태로 번호에 맞게 가져오기
                Toast.makeText(parent.context, "이름 : ${profile.name} 나이 : ${profile.age} 직업 : ${profile.job}", Toast.LENGTH_LONG).show()
            }
        }
    //뷰홀더에 뷰를 넘겨주고 이 것을 반환한다.
    }

    //재활용해주는 곳 및 값을 넣어주는 곳
    override fun onBindViewHolder(holder: ProfileAdapter.CustomViewHolder, position: Int) {
       holder.profile.setImageResource(profileList.get(position).profile)
        holder.name.text = profileList.get(position).name
        holder.age.text = profileList.get(position).age.toString() // 인트형이기 때문에
        holder.job.text = profileList.get(position).job

    }

    //리스트의 갯수를 적어준다
    override fun getItemCount(): Int {
        return profileList.size
    }

    //뷰홀더 클래스(음료수처럼 잡아주는 홀더)
    //이곳에서 파인드뷰아이디로 리스트 아이템에 있는 뷰들을 참조한다.
    inner class CustomViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {
        val profile = itemView.findViewById<ImageView>(R.id.imageView) //사진
        val name = itemView.findViewById<TextView>(R.id.textView) //이름
        val age = itemView.findViewById<TextView>(R.id.ageView) //나이
        val job = itemView.findViewById<TextView>(R.id.jobView) //직업
    }
}

위에서부터 보면 ProfileAdapter가 Profiles(틀) 형태로

 

ArrayList로 값을 받으려고 하고 RecyclerView.Adapter를 상속받고,

 

이제 만들 CustomViewHolder를 제너릭에 넣어준다.

 


커스텀 뷰 홀더 

 

그 후 먼저 맨 아래에 CustomViewHolder를 만들어 준다.

 

여기서 inner class란 내부의 클래스라는 뜻으로 붙여준다.

 

그냥 class를 써도 되지만, 그냥 class를 쓰면 static처리가 되어,

 

쓸데없이 메모리를 잡아먹게 된다(메모리 릭으로 이어질 수 있다)

 

뷰를 인자로 담은 리사이클 러뷰를 상속으로 받는다.

 

이렇게 되면 이제 list_item에 있는 뷰들의 아이디 값을 참조할 수 있게 된다.

 

싹 다 참조해준다.

 


OnCreateViewHolder

이 메서드에서는 뷰 홀더가 맨 처음 생성될 때 호출되는 클래스로 

 

이제 아이디 값을 참조한 CustomViewHolder에

 

인플레이션 한 뷰(아까 만든 list_item)를 인자로 넣어

 

새로운 뷰를 리턴 받습니다.

(리 사이클러 뷰 아이템 생성)

 

이렇게 뷰 값이 어뎁터로 전달될 때마다 새로운 뷰가 만들어지는 곳이라고 할 수 있습니다.

 

여기까지만 아셔도 되지만,

 

이 return 받는 뷰에서 apply를 붙이고

 

itemview를 참조하는 동시 클릭 리스너를 붙이고,

 

각 아이템을 클릭할 때마다 액션을 지정하실 수 있습니다.

 

추가로 apply란 

 

함수를 호출하는 객체를 이어지는 블록의 리시버로 전달하고, 객체 자체를 반환해줍니다.

 

여기서 리시버란, 바로 이어지는 블록 내에서 메서드 및 속성에 바로 접근할 수 있도록 할 객체를 의미합니다.

 

예를 들어 특정 객체를 생성하면서 동시에 호출해야 하는 초기화 코드가 있는 경우에 사용할 수 있습니다.

 

바로 지금과 같은 상황이죠,

 

새롭게 onCreateViewHolder가 만들어지는 초기화 상황에서 말이죠.

 

getItemCount는 간단히 말해 뷰를 몇 개 호출할지 지정해주는 메서드입니다.

 


OnBindViewHolder

이곳에서는 뷰에 값을 넣어주는 곳입니다.

 

인자에 있는 position은 각 뷰 홀더에 위치에 맞는 값을 전달해주기 위해

 

존재합니다.

 

우선 홀더에 CustomViewHolder를 참조하여 뷰 홀더에 있는 (아까 참조한 아이디)

 

값들에 다가 각각 값을 넣어주는데,

 

Adpater자체에 인자로 넘어온 profileList에서 값들을 꺼내서 입력합니다.

 

그러면 이 profileList를 만들어서 이 어뎁터에 보내 봅시다.

 

다시 MainActivity로 넘어갑니다.

 

class MainActivity : AppCompatActivity() {
    private var mBinding : ActivityMainBinding? = null
    private val binding get() = mBinding!!
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val profileList = arrayListOf(
            Profiles(R.drawable.ic_launcher_background, 26, "수열백", "안드개발자"),
            Profiles(R.drawable.ic_launcher_background, 245, "dsad", "안드개발자"),
            Profiles(R.drawable.ic_launcher_background, 25, "sadqwf", "안드개발자"),
            Profiles(R.drawable.ic_launcher_background, 254, "vadsv", "안드개발자"),
            Profiles(R.drawable.ic_launcher_background, 452, "vdsav", "안드개발자"),
            Profiles(R.drawable.ic_launcher_background, 25, "sdvaw", "안드개발자"),
        )
        binding.re.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
        //레이아웃매니저를 이용해 어뎁터의 방향을 결정
        binding.re.setHasFixedSize(true)//어뎁터에 성능을 위한것
        binding.re.adapter = ProfileAdapter(profileList) //어뎁터에 리스트 자료를 넣는다.
    }
}

 

우선 arrayListOf형태로 profileList라는 변수에 담습니다.

 

그리고 그 안에다가 아까 저희가 만든 Profiles(틀)을 활용합니다.

 

이런 식으로 형식에 맞는 값을 넣고,

 

저 맨 아래에 

 

ProfileAdpater(아까 위에서 만든 어뎁터입니다.)에다가 인자로 보내줍니다.

 

만약 서버를 이용하신다면 저 arrayListOf의 값을 잘 이용하시면 될 겁니다.

 

 

이렇게 되면 어뎁터는 잘 마무리가 될 것입니다!!

 

아 그리고 추가로 저 리니어 레이아웃 매니저는 리사이클러뷰의 방향을 설정해주는 녀석으로

 

현재는 버티컬로 되어있지만, 원하시면 호리즈날로 변경 가능하십니다.

 

setHasFixedSize는 보통 기능적인 면을 위해 리사이클러뷰에 넣는 속성 중 하나입니다.

 

더 자세히 설명드리자면

 

기본적으로 리사이클 러뷰의 아이템의 항목을 추가하거나 이동하거나 제거가 될 때,

 

리사이클러뷰의 크게 변경될 수도 있습니다.

 

그래서 항목을 자주 추가하거나 제거하면 문제가 되는데,

 

setHasFixedSize를 true로 줌으로 써 어뎁터의 내용을 변경할 때

 

높이와 너비가 변경되지 않게 할 수 있는 기능입니다.

 

여기까지 리사이클 러뷰의 코틀린 버전 설명이었습니다.

 

저도 아직 초보라 모르는 게 많아 혹시 틀린 점이 있다면 피드백 부탁드립니다.

 

감사합니다.

 

 

 

728x90
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading