본문 바로가기
Android

RecyclerView 란?

by 안솝우화 2021. 4. 3.
반응형

RecyclerView 탄생 배경!

RecyclerView는 기존에 있던 ListViews 및 GridViews의 단점을 인식했습니다

문제는 메모리가 매우 비효율적이었고 복잡하고 오류가 발생하기 쉬운 코드를 자주 작성해야했습니다

때문에 RecyclerView 나오게 되었습니다

 

 


 

RecyclerView를 사용해 보자

activity_main.xml 에 recyclerview를 추가 해줍니다

참고로 이 글에서는 databinding을 사용합니다

<?xml version="1.0" encoding="utf-8"?>
<layout 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">


    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/my_recycler_view"
            android:layout_width="0dp"
            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>
</layout>

추가 했다면 MainActivity.kt 로 가서 바인딩을 선언해줍니다

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    }

}

 

linearlayout 관리자를 설정 해줍니다

        binding.myRecyclerView.layoutManager = LinearLayoutManager(this)

 

MyRecyclerViewAdapter.kt 파일을 만들어 줍니다

그후 class에 :RecyclerView.Adapter<>()를 추가 해줍니다

class MyRecyclerViewAdapter:RecyclerView.Adapter<>() {
}

아직 오류가 나는게 정상입니다, 당황하지 마세요!

아래에 MyViewHolder 클래스를 선언해줍니다

class MyViewHolder(val view: View) : RecyclerView.ViewHolder(){
}

별도의 파일에서 이 작업을 할수 있지만 ViewHolder클래스를 사용하는 것이 훨씬 쉽습니다

다시 MyRecyclerViewAdapter 클래스로 돌아가서 아까 비워두었던 

:RecyclerView.Adapter<>() 이곳의 <>안에 MyViewHolder 넣어줍니다

class MyRecyclerViewAdapter:RecyclerView.Adapter<MyViewHolder>()

 

Adapter(어댑터) 클래스의 3가지 주요 기능은 onCreateViewHolder, onBindViewHolder, getItemCount 입니다

이 메서드를 모두 추가하려면 class MyRecyclerViewAdapter의 빨간줄에 커서를 가져가서 전구 버튼을 누르고 

implement members를 눌러 3가지 모두 추가해줍니다(다중 선택 단축키 : ctrl 누른상태 클릭)

그러면 이런 코드가 생기게 될겁니다, TODO는 지워도 상관없습니다

class MyRecyclerViewAdapter:RecyclerView.Adapter<MyViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    
    }
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    }

    override fun getItemCount(): Int {
    }

}

그 다음 recyclerview에 표시될 아이템을 만들어 주겠습니다

res -> layout로 가서 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:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="30dp"
    android:background="#E37777"
    android:orientation="vertical">

    <TextView
        android:id="@+id/name_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:textColor="@color/white"
        android:textSize="30sp"
        android:layout_margin="30dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

그 다음 다시 MyRecyclerViewAdapter.kt로 돌아옵니다

onCreateViewHolder안에 추가해줍니다

        val layoutInflater = LayoutInflater.from(parent.context)
        val listItem = layoutInflater.inflate(R.layout.list_item,parent,false)


그리고 나서 onCreateViewHolder에서 리사이클러뷰 라이브러리는 뷰홀더 인스턴스를 반환해야 합니다
위에 :MyViewHolder이 인스턴스고 우리가 방금 만든 MyViewHolder 클래스는 ViewHolder 클래스의 하위 클래스입니다
따라서 MyViewHolder ViewHolder 이고 뷰가 매개 변수로 표시되는 생성자를 포함합니다
때문에 우리는 MyViewHolder MyViewHolder(listItem)을 return 해줍니다

        return MyViewHolder(listItem)

이어서 MyViewHolder 클래스로 가서 ViewHolder에 인수로(view) 를 전달해 줍니다

class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view){  //<-이곳
}

 

getItemCount메서드는 어탭터가 보유한 데이터 세트의 총 항목 수를 리턴 해줍니다

우선 5개를 리턴해 줍니다

 override fun getItemCount(): Int {
        return 5
    }

다시 MainActivity.kt로 돌아가 MyRecyclerViewAdapter 클래스를 RecyclerView의 어댑터로 설정합니다

        binding.myRecyclerView.adapter = MyRecyclerViewAdapter()

현재 전체 코드


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout 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">


    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/my_recycler_view"
            android:layout_width="0dp"
            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>
</layout>

MainActivity.kt

package com.example.recyclerview

import android.database.DatabaseUtils
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.recyclerview.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.myRecyclerView.setBackgroundColor(Color.YELLOW)

        //linearlayout관리자 설정
        binding.myRecyclerView.layoutManager = LinearLayoutManager(this)

        //MyRecyclerViewAdapter 클래스를 RecyclerView의 어댑터로 설정
        binding.myRecyclerView.adapter = MyRecyclerViewAdapter()

    }

}

MyRecyclerViewAdapter.kt

package com.example.recyclerview

import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.recyclerview.databinding.ListItemBinding

//어댑터 클래스의 세 가지 주요 기능
//onCreateViewHolder, onBindViewHolder, getItemCount
class MyRecyclerViewAdapter:RecyclerView.Adapter<MyViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val listItem = layoutInflater.inflate(R.layout.list_item,parent,false)

        //onCreateViewHolder에서 리사이클러뷰 라이브러리는 뷰홀더 인스턴스를 반환해야함
        //위에 : MyViewHolder이 인스턴스
        //여기서 만든 MyViewHolder 클래스는 ViewHolder 클래스의 하위 클래스이다
        //따라서 MyViewHolder는 ViewHolder 입니다. 뷰가 매개 변수로 표시되는 생성자를 포함합니다.
        //따라서 MyViewHolder로 return 해줍니다
        return MyViewHolder(listItem)
    }


    //onBindViewHolder를 사용하여 데이터를 표시
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    }

    //어댑터가 보유한 데이터 세트의 총 항목 수 리턴
    override fun getItemCount(): Int {
        return 5
    }

}

//별도의 파일에서 이 작업을 할수 있지만 ViewHolder클래스를 사용하는 것이 훨씬 쉽다
class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view){
    //위쪽에서 MyViewHolder 클래스에서 ViewHolder에 인수로 뷰를 전달해야 하기 때문에 (view)를 썼다


}

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<layout 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">


    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:background="#E37777"
        android:orientation="vertical">

        <TextView
            android:id="@+id/name_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="30dp"
            android:text="TextView"
            android:textColor="@color/white"
            android:textSize="30sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

자 이제 실행해 봅시다!

이런 화면이 나오게 될겁니다

 

여기서 우리는 각 항목당 번호를 세는 방법은 무엇인지 궁금해하게 될 겁니다(그렇다고 해주세요;;)

 

다시 MyRecyclerViewAdapter.kt로 돌아가 onBindViewHolder메서드로 가줍니다

        holder.view.findViewById<TextView>(R.id.name_text_view).text = "Hello from onBindViewHolder $position"

 

이 코드를 추가해 줍니다. position은 목록 항목의 위치 값을 나타냅니다. 다시 실행하게 되면 이런 모습이 나옵니다

 

 

 


 

간단한 예제

간단한 과일 이름 목록 나열 예제를 만들어 봅시다 (코드는 앞에 코드에 이어서 작성하시면 됩니다!)

Fruit라는 data class를 만들어줍니다

package com.example.recyclerview

data class Fruit (val name: String, val supplier:String)

MainActivity.kt에 리스트를 정의 해줍니다. 이곳에서 정의 하는게 좋은 습관입니다 (일반적으로 뷰 컨트롤러에서 어댑터로 데이터를 보냅니다)

    val fruitsList = listOf(Fruit("Mango","tom"),Fruit("Apple","jom"),Fruit("Banana","hom"),Fruit("Guava","jom"),Fruit("Lemon","kom"),Fruit("Pear","oom"),Fruit("Orange","koa"))

MyRecyclerViewAdapter.kt로 가서 fruitslist를 생성자 매개 변수로 추가해줍니다

class MyRecyclerViewAdapter(val fruitsList:List<Fruit>):RecyclerView.Adapter<MyViewHolder>() 

MainActivity.kt에서 MyRecyclerViewAdapter에 fruitslist를 생성자 매개 변수로 추가했기 때문에 인수로 fruitsList를 넘겨줍니다

        binding.myRecyclerView.adapter = MyRecyclerViewAdapter(fruitsList) //기존에 ()안에 fruitsList추가

다시 MyRecyclerViewAdapter.kt로 가서 onBindViewHolder에 text부분을 수정해줍니다

        holder.view.findViewById<TextView>(R.id.name_text_view).text = fruit.name

 


추가사항

좀더 객체지향 프로그래밍적으로 코딩하는 방법

코드를 조금 수정해 줍니다

MyRecyclerViewAdapter.kt의 onBindViewHolder 메서드 부분

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bind(fruitsList[position])

    }

MyRecyclerViewAdapter.kt의 MyViewHolder 클래스 부분

class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view){
    //위쪽에서 MyViewHolder 클래스에서 ViewHolder에 인수로 뷰를 전달해야 하기 때문에 (view)를 썼다

    fun bind(fruit: Fruit){
        view.findViewById<TextView>(R.id.name_text_view).text = fruit.name

    }


}

 

fruit.name, fruit.supplier 둘다 가능합니다(data class)

이제 실행 해 보겠습니다

원하는대로 잘 나오는걸 확인할수 있습니다!

반응형