[Android/Kotlin] RecyclerView Duplication - 중첩(이중) 리사이클러뷰

Notepad96

·

2022. 9. 6. 19:55

300x250

 

 

1. 요약

 

결과

 

 

이번 글에서는 중첩(이중) RecyclerView를 구현하는 방법에 관하여 알아본다.

 

이중 RecyclerView 같은 경우에는 여러 애플리케이션들한테 볼 수 있듯이 자주 사용하는 구성이며 분류에 따라 데이터들을 나눠서 보여줘야 하는 경우 유용하게 사용이 가능하다.

 

 

 

2. 레이아웃

2-1. activity_main.xml

메인 레이아웃으로서 중첩되는 RecyclerView 중 우선 바깥쪽에 RecyclerView를 정의한다.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">

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

</LinearLayout>

 


2-2. item_list_01.xml

메인 레이아웃에서 정의하였던 바깥쪽 RecyclerView에 항목 레이아웃으로서 레이아웃 구성을 보면 RecyclerView가 들어가 있다.

 

이 RecyclerView서 마찬가지로 레이아웃과 어댑터를 구성하여 RecyclerView를 보여주면 이중 RecyclerView를 나타낼 수 있다.

 

item_list_01

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_marginVertical="10dp">

    <TextView
        android:id="@+id/textView01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:text="Text"
        android:textSize="22sp"
        android:textStyle="bold" />

    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:layout_marginRight="20dp"
        android:layout_marginLeft="5dp"
        android:background="@color/black" />


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

</LinearLayout>

 


2-3. item_list_02.xml

item_list_01 레이아웃서 정의되어 있던 RecyclerView의 사용항 항목 레이아웃이다.

 

안쪽 RecyclerView에 레이아웃으로서 ImageView 1개와 TextView 1개를 사용하여 구성하였다.

 

item_list_02

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:elevation="10dp"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/imageView01"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:scaleType="fitXY"
        />

    <TextView
        android:id="@+id/textView02"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:background="@drawable/border_bottom_corner"
        android:text="Test"
        android:textSize="18sp"
        android:textColor="@color/black"
        android:gravity="center" />

</LinearLayout>

 

 

 

 

3. 코드 및 설명

3-1. MainActivity.kt

메인 레이아웃에서 정의하였던 RecyclerView를 초기화해준다.

 

Adapter로는 바깥쪽 RecyclerView에서 사용하기 위해서 정의한 ListAdapter01를 사용하며 리스트의 방향은 디폴트 값인 세로형으로 만든다.

 

 

package com.notepad96.recyclerviewduplication

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.notepad96.recyclerviewduplication.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private val binding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }

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

        binding.recycler01.apply {
            adapter = ListAdapter01()
            layoutManager = LinearLayoutManager(context)
            setHasFixedSize(true)
        }
    }
}

 


3-2. ListAdapter01.kt

바깥쪽 RecyclerView의 사용하기 위하여 정의한 Adapter로서 임시 data로서 Map<String, List<String>> 타입의 변수를 정의하였다.

 

 

bind 함수를 통하여 각 뷰 데이터를 초기화해주며, 앞서 item_list_01 레이아웃서 정의한 뷰 오브젝트를 살펴보면 TextView 1개와 RecyclerView 1개가 있다.

 

여기서 TextView에는 data의 Key값을 RecyclerView 서는 메인에서 초기화하였듯이 ListAdapter02 Adapter를 사용하여 RecyclerView를 초기화해주고 있다.

 

이때 RecyclerView의 LinearLayoutManager 인수값을 보면 리스트의 방향을 HORIZONTAL, 가로 쪽으로 정의해주었다.

 

package com.notepad96.recyclerviewduplication

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.notepad96.recyclerviewduplication.databinding.ItemList01Binding

class ListAdapter01: RecyclerView.Adapter<ListAdapter01.MyView01>() {
    private val data = mapOf(
        "빵" to listOf("빵1", "빵2", "빵3"),
        "고양이" to listOf("고양이1", "고양이2", "고양이3", "고양이4"),
        "쿠키" to listOf("쿠키1", "쿠키2", "쿠키3", "쿠키4", "쿠키5"),
        "강아지" to listOf("강아지1", "강아지2", "강아지3", "강아지4", "강아지5")
    )

    inner class MyView01(private val binding: ItemList01Binding): RecyclerView.ViewHolder(binding.root) {
        fun bind(pos: Int) {
            binding.textView01.text = data.keys.elementAt(pos)
            binding.recycler02.apply {
                adapter = ListAdapter02(data.values.elementAt(pos), pos)
                layoutManager = LinearLayoutManager(binding.recycler02.context, LinearLayoutManager.HORIZONTAL, false)
                setHasFixedSize(true)
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyView01 {
        val view = ItemList01Binding.inflate(LayoutInflater.from(parent.context), parent, false)
        return MyView01(view)
    }

    override fun onBindViewHolder(holder: MyView01, position: Int) {
        holder.bind(position)
    }

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

 


3-3. ListAdapter02.kt

안쪽 RecyclerView에서 사용할 Adapter를 정의한 파일이다.

 

마찬가지로 bind 함수를 통하여 뷰의 접근하여 데이터를 초기화해주고 있다.

 

 

Adapter의 인자로 List를 받으며 해당 예시에서는 ListAdapter01에서 정의하였던 data의 List 타입을 갖는 values를 사용하였다.

 

 

package com.notepad96.recyclerviewduplication

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.notepad96.recyclerviewduplication.databinding.ItemList02Binding

class ListAdapter02(private val data: List<String>, private val parentPos: Int): RecyclerView.Adapter<ListAdapter02.MyView02>() {
    val images = listOf(R.drawable.bread, R.drawable.cat, R.drawable.cookie, R.drawable.dog)

    inner class MyView02(private val binding: ItemList02Binding): RecyclerView.ViewHolder(binding.root) {
        fun bind(pos: Int) {
            binding.imageView01.setImageResource(images[parentPos])
            binding.textView02.text = data[pos]
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyView02 {
        val view = ItemList02Binding.inflate(LayoutInflater.from(parent.context), parent, false)
        return MyView02(view)
    }

    override fun onBindViewHolder(holder: MyView02, position: Int) {
        holder.bind(position)
    }

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

 

 

 

 

4. 전체 파일

 

 

 

GitHub - Notepad96/BlogExample02

Contribute to Notepad96/BlogExample02 development by creating an account on GitHub.

github.com

 

 

 

300x250