본문 바로가기
ⓢⓣⓤⓓⓨ/ⓐⓝⓓⓡⓞⓘⓓⓢⓣⓤⓓⓘⓞ

[Kotlin] 리사이클러 뷰 (RecyclerView)

by heaven00 2021. 8. 25.
728x90

 

 

 

 

출력화면

 

 

 

 

리사이클러 뷰 란?

이전에는 ListView가 리스트뷰를 통해 여러 목록을 표현해주는데 사용했다.

하지만 리스트뷰를 스크롤할 때마다 화면에서 사라지는 가장 위의 아이템을 삭제하고 가장 아래에 아이템을 생성하면 횟수가 많아질수록 생성과 삭제의 반복으로 인해 낭비가 심하다

 

따라서 이를 해결하기 위해 Recycler view가 등장했고, 이 리사이클러 뷰는 가장 위의 아이템을 삭제하지 않고 가장 아래의 아이템쪽으로 객체를 이동시켜 재사용을 할 수 있도록 고안되었다. 

 

즉, ListView보다 효율적으로 리스트를 관리할 수 있는 것이 RecyclerView이다.

 

 

 

 


<안드로이드 원서>


RecyclerView를 사용하면 대용량 데이터 세트를 효율적으로 쉽게 표시할 수 있습니다. 데이터를 제공하고 각 항목의 모양을 정의하면 RecyclerView 라이브러리가 필요할 때 요소를 동적으로 생성합니다.

이름에서 알 수 있듯이 RecyclerView  이러한 개별 요소를 재활용 합니다. 항목이 화면 밖으로 스크롤될 때 RecyclerView는 해당 보기를 파괴하지 않습니다. 대신 RecyclerView는 화면에서 스크롤된 새 항목에 대해 보기를 재사용합니다. 이렇게 재사용하면 성능이 크게 향상되어 앱의 응답성이 향상되고 전력 소비가 줄어듭니다.

 

 

 

 

즉, 위의 그림과 같이 파괴하지 않고 재사용한다는 것이다!

 

 

 

 

리사이클러뷰 만드는 순서

 

(1) 리사이클러뷰가 들어갈 XML 레이아웃 정의

  - 리사이클러뷰에 들어갈 각 아이템의 레이아웃을 XML로 정의

    => 아래와 같이 리사이클러뷰가 들어갈 XML을 정의해주는 것이다.

     단순히 recyclerview를 넣어주기만하면 된다.

 

 

 

 

 

(2) RecyclerView에 각각의 데이터 항목을 표현하기 위한 레이아웃 정의

  - 리사이클러뷰에 들어갈 각 아이템을 하나의 뷰로 정의

     => 아래와 같이 리스트에 들어갈 아이템에 대한 디자인을 정의해주는 것이다.

 

 

참고로 텍스트를 굵게하고 색상을 바꾼 것이 있다.

이름의 textStyle은 bold, 나이의 textColor는 blue를 했다

차례대로 원하는 태그 사이에

android:textStyle="bold"

android:textColor="#2196F3"

를 추가해줬다

 

아래는 디자인 탭에서 수정하는 방법이다.

 

 

 

 

 

(3) 모델 객체 정의

  - 리스트에 들어갈 데이터에 어떤 요소가 들어갈 것인지 정의해주는 것

ex) 위와 같은 형식으로 item을 만든다면, 프로필(사진)과 이름, 나이, 직업 요소가 들어갈 것이라는 큰 형태를 정의해주는 것

 

 

 

 

(4) 어뎁터 직접 생성

  - 어뎁터를 생성하고, 위에서 선언한 객체의 요소에 맞게 화면에 뿌려질 수 있도록 함

 

 

 

 

(5) 어뎁터와 리스트뷰 연결

  - MainActivity.kt 에서 연결해주면 된다.

 

 

 

 

 

 

위의 차례에 맞게 소스를 공유하면,

 

 

 

 

 

 

(1) 리사이클러뷰가 들어갈 XML 레이아웃 정의 (activity_main.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:id="@+id/webView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_profile"
        android:layout_width="409dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

 

 

 

 

 

 

 

(2) ListView에 각각의 데이터 항목을 표현하기 위한 레이아웃 정의 (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_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0">

        <ImageView
            android:id="@+id/iv_profile"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_marginStart="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/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="24dp"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            android:text="이름1"
            android:textStyle="bold"
            app:layout_constraintBottom_toTopOf="@+id/tv_job"
            app:layout_constraintStart_toEndOf="@+id/iv_profile"
            app:layout_constraintTop_toTopOf="@+id/iv_profile" />

        <TextView
            android:id="@+id/tv_job"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="24dp"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            android:text="개발자"
            app:layout_constraintBottom_toBottomOf="@+id/iv_profile"
            app:layout_constraintStart_toEndOf="@+id/iv_profile"
            app:layout_constraintTop_toBottomOf="@+id/tv_name" />

        <TextView
            android:id="@+id/tv_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:text="27"
            android:textColor="#2196F3"
            app:layout_constraintBottom_toBottomOf="@+id/tv_name"
            app:layout_constraintStart_toEndOf="@+id/tv_name"
            app:layout_constraintTop_toTopOf="@+id/tv_name" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

 

 

 

 

 

 

 

 

(3) 모델 객체 정의 (Profiles.kt)

 

package com.example.navigationkt

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

 

 

 

 

 

 

 

(4) 어뎁터 직접 생성 (ProfileAapter.kt)

 

package com.example.navigationkt

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView

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

    //list 아이템을 어뎁터에 붙여줌
    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 {
                val curPos : Int = adapterPosition
                val profile: Profiles = profilList.get(curPos)
                Toast.makeText(parent.context, "이름: ${profile.name}  나이: ${profile.age} 직업: ${profile.job}", Toast.LENGTH_SHORT).show()

            }

        }
    }

    //실제 총 연결 -> 지속적으로 호출되면서 모든 데이터 매치시켜줌
    override fun onBindViewHolder(holder: ProfileAdapter.CustomViewHolder, position: Int) {
        holder.gender.setImageResource(profilList.get(position).gender)
        holder.name.text = profilList.get(position).name
        holder.age.text = profilList.get(position).age.toString()
        holder.job.text = profilList.get(position).job
    }

    //리스트 총 개수
    override fun getItemCount(): Int {
        return profilList.size
    }

    class CustomViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val gender = itemView.findViewById<ImageView>(R.id.iv_profile) //성별
        val name = itemView.findViewById<TextView>(R.id.tv_name)    //이름
        val age = itemView.findViewById<TextView>(R.id.tv_age)    //나이
        val job = itemView.findViewById<TextView>(R.id.tv_job)    //직업

    }

}

만약 리스트 데이터 하나를 클릭했을 때, 토스트메시지로 해당 정보를 띄우고 싶으면 이 class에서 선언해야한다. 

 

 

 

 

 

 

 

 

(5) 어뎁터와 리사이클러뷰 연결 (MainActivity.kt)

 

package com.example.navigationkt

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MenuItem
import android.widget.LinearLayout
import android.widget.Toast
import androidx.core.view.GravityCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.navigationkt.databinding.ActivityMainBinding
import com.google.android.material.navigation.NavigationView

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_baseline_accessibility_24, "이름0", 22, "개발자0" ),
            Profiles(R.drawable.ic_baseline_accessibility_24, "이름1", 23, "개발자1" ),
            Profiles(R.drawable.ic_baseline_accessibility_24, "이름2", 24, "개발자2" ),
            Profiles(R.drawable.ic_baseline_accessibility_24, "이름3", 25, "개발자3" ),
            Profiles(R.drawable.ic_baseline_accessibility_24, "이름4", 26, "개발자4" ),
            Profiles(R.drawable.ic_baseline_accessibility_24, "이름5", 27, "개발자5" ),
            Profiles(R.drawable.ic_baseline_accessibility_24, "이름6", 28, "개발자6" ),
            Profiles(R.drawable.ic_baseline_accessibility_24, "이름7", 29, "개발자7" )

        )

        binding.rvProfile.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
        binding.rvProfile.setHasFixedSize(true)

        binding.rvProfile.adapter = ProfileAdapter(profileList)



    }

    //액티비티가 종료되는 시점이 다가올 때 호출
    override fun onDestroy() {
        super.onDestroy()
    }

}

 

리스트에 들어갈 데이터를 임의로 넣은 것이고, 이미지도 이미 저장되어 있는 것을 사용했다.

 

 

 

 

 

 

 

 

 

 

 


 

 

리스트뷰를 만드는 과정이 조금 헷갈렸는데,

리사이클러뷰를 다시 한번 배우고 나니 두개 다 확실히 이해가 되는 것 같다. 

어디서든 꼭 쓰이는 것이 리사이클러뷰라고 하니, 꼭 이해하고 넘어가자!

 

 

 

 

 

위의 내용은 '홍드로이드' 님의 유튜브 강의를 보고 공부한 내용입니다.

코드를 변형한 부분도 있고 제가 이해한 방식대로 주석이나 추가 설명을 넣었습니다.

728x90

댓글