땀이 삐질삐질 나는 개발 일기

[안드로이드] 기본적인 Recyclerview ViewType으로 홀더 나누기, 다른 Layout만들기 본문

개발 Tip

[안드로이드] 기본적인 Recyclerview ViewType으로 홀더 나누기, 다른 Layout만들기

삐질 2020. 4. 16. 20:22

안녕하세요 개발자 삐질입니다

 

오늘 제가 소개하고 싶은 내용은 리사이클러뷰를 다루다 보면, 칸칸이 다른 Row를 표현해야할 때가 옵니다.

그때를 위한 ViewType을 사용해 표현하는 방법 입니다.

 

간략하게 개념을 설명 드리겠습니다.

리사이클러뷰는 각 칸마다 해당 칸의 ViewType, 즉 해당 칸(Row"이하 칸")의 특성을 나타내는 ViewType이란 것을

기반으로 어떤 Layout을 형성할지 onCreateViewHolder에서 판단한 뒤, Holder를 return하게 됩니다.

기본적으로 이 ViewType의 Value는 개발자가 직접 지정할 수 있으며, 지정하지 않았을때는 내부적으로 Default value로 처리가 됩니다.

 

즉 

1.빨 ViewType =1

2.주 ViewType =2 

3.노 ViewType =3

4.초 ViewType =4

5.파 ViewType =5

6.남 ViewType =6

7.보 ViewType =7

과 같이 저는 ArrayList각 아이템에 ViewType을 지정하겠습니다.

 

리사이클러뷰는 각 칸의 데이터를 List형태로 관리하기 때문에 해당 List(샘플 데이터)를 만들기 위해 

Model과 ViewType을 가지는 ItemEntity클래스를 만들어줬습니다.

 

ViewHolder 라는 것은 말 그대로 View를 담는 그릇입니다 RecyclerView에서는 칸칸별로 Layout을 핸들링 하기 위한 Holder를 강제하고 있습니다. ViewHolder에 대한 기본 개념은 추가적으로 공부하시기 바랍니다.

 

 

 

 

저는 기본적으로 FindViewById를 사용하지 않습니다. DataBinding을 사용해 Id에 접근합니다.

Gradle :App 에 아래와 같이 추가해주시면 됩니다.

 

dataBinding{
    enabled= true
}

 


class Model {

    init {
    }

    //각자 다른 뷰타입을 아이템 7개에 순차적으로  Insert
    fun makeTestEntities(): ArrayList<Item> {
        var items = ArrayList<Item>()
        for (i in 1..7) {
            var item =
                Item(i)
            items.add(item)
        }

        return items
    }


    data class Item(
        var viewType: Int = 0
    ) {

    }
}

 

 

이후 모델은 Item클래스의 객체를 7개의 List를 만들기 위한 Function을 만들어 주었습니다.

이 Function은 호출 시, 7개의 Item타입을 가지고있는 ArrayList를 반환하게 됩니다.

 

이후 RecyclerView Adatper를 커스텀 해주었습니다.

 

class ItemsAdatper(private var items: ArrayList<Model.Item>?) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    //각 뷰타입 별 분기 -> 다른 홀더와 다른 Layout을 Inflate해줌
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        when (viewType) {
            TYPE_RE -> {
                var binding =
                    ItemType01Binding.inflate(LayoutInflater.from(parent.context), parent, false)
                return ItemRedHolder(
                    binding
                )
            }
            TYPE_OR -> {
                var binding =
                    ItemType02Binding.inflate(LayoutInflater.from(parent.context), parent, false)
                return ItemOrangeHolder(
                    binding
                )
            }
            TYPE_YELL -> {
                var binding =
                    ItemType03Binding.inflate(LayoutInflater.from(parent.context), parent, false)
                return ItemYellowHolder(
                    binding
                )
            }
            TYPE_GR -> {
                var binding =
                    ItemType04Binding.inflate(LayoutInflater.from(parent.context), parent, false)
                return ItemGreenHolder(
                    binding
                )
            }
            TYPE_BL -> {
                var binding =
                    ItemType05Binding.inflate(LayoutInflater.from(parent.context), parent, false)
                return ItemBlueHolder(
                    binding
                )
            }
            TYPE_DEPPBL -> {
                var binding =
                    ItemType06Binding.inflate(LayoutInflater.from(parent.context), parent, false)
                return ItemDBlueHolder(
                    binding
                )
            }
            TYPE_PU -> {
                var binding =
                    ItemType07Binding.inflate(LayoutInflater.from(parent.context), parent, false)
                return ItemPupleHolder(
                    binding
                )
            }
            else -> {
                var binding =
                    ItemType01Binding.inflate(LayoutInflater.from(parent.context), parent, false)
                return ItemRedHolder(
                    binding
                )
            }

        }
    }

    // 어댑터가 전달받은 리스트 사이즈 반환 , Null일땐 0
    override fun getItemCount(): Int {
        return items?.size ?: 0
    }

    //홀더 타입에 따라 뷰와 바인딩 해줌
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is ItemRedHolder) {
            holder.bind(items?.get(position))
        } else if (holder is ItemOrangeHolder) {
            holder.bind(items?.get(position))
        } else if (holder is ItemYellowHolder) {
            holder.bind(items?.get(position))
        } else if (holder is ItemGreenHolder) {
            holder.bind(items?.get(position))
        } else if (holder is ItemBlueHolder) {
            holder.bind(items?.get(position))
        } else if (holder is ItemDBlueHolder) {
            holder.bind(items?.get(position))
        } else if (holder is ItemPupleHolder) {
            holder.bind(items?.get(position))
        }
    }

    //각 아이템의 Viewtype을 반환함 여기서 반환 된 VieType-> onCreateViewHolder로 넘어감
    override fun getItemViewType(position: Int): Int {
        items?.let {
            return it[position].viewType
        } ?: kotlin.run {
            return super.getItemViewType(position)
        }
    }


    //뷰타입 종류
    companion object {
        const val TYPE_RE = 1;
        const val TYPE_OR = 2;
        const val TYPE_YELL = 3;
        const val TYPE_GR = 4;
        const val TYPE_BL = 5;
        const val TYPE_DEPPBL = 6;
        const val TYPE_PU = 7;

    }

}

 

리사이클러뷰 어댑터에서는 기본적으로 우리가 알고있는 onCreateViewHolder / getItemCount / onBindViewHolder 외 getItemViewType이라는 Override Function을 추가로 재정의 합니다.

이 Function에는 제가 위에서 만들어 Adapter클래스로 전달해 준 ArrayList의 각 칸의 ViewType을 반환합니다.

 

 

 

이렇게 Model / ItemEntity / Adapter가 정의 되었다면 실제로 Recyclerview에 대한 설정을 위해 Activity에서 아래와 같이 작업 했습니다.

 


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

    fun init() {
        model = Model() // 데이터를 핸들링 하기 위한 Model Init
        setRecyclerView() //리사이클러뷰 Init
    }

    fun setRecyclerView() {
        adapter =
            ItemsAdatper(model?.makeTestEntities())
        var layoutManager = LinearLayoutManager(this)
        layoutManager.orientation = RecyclerView.VERTICAL
        binding.recyclerview.layoutManager = layoutManager
        binding.recyclerview.adapter = adapter
    }
}

 

참 쉽쥬? 

 

onCreate가 호출되게 되면, inti -> setRecyclerView()를 거쳐 RecyclerView가 Adatper를 Setted하게 되고 실제 아이템을 그리게 됩니다.

예제이기 때문에 목적별 라이프사이클에 대한 Function의 위치는 무시해주세요.

 

 

그렇게 되면 아래와 같은 View를 그리게 됩니다.

 

 

 

이렇듯 RecyclerView는 칸칸이 다른 레이아웃(뷰홀더)을 관리할 수 있게 합니다.

 

 

 

 

-----------------------------------샘플 ItemHolder xml -----------------------------

Item_type01.xml

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="#FA0909">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="빨강색"
            android:gravity="center"
            android:textColor="#ffffff"
            android:textSize="40sp"></TextView>
    </LinearLayout>
</layout>

 

Item_type02.xml

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="#F46708">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="주황색"
            android:textColor="#ffffff"
            android:textSize="40sp"></TextView>
    </LinearLayout>
</layout>

 

 

 

 

 

해당 샘플의 코드는 아래 git에 가시면 볼수 있습니다.

https://github.com/jaekanglee/RecyclerViewType

 

 

초급 안드로이드 개발자를 위한 카카오톡 오픈 채팅방을 운영 중 입니다. 
(저는 마냥 친절한 방장은 아닙니다.  다만, 다 같이 성장하고 싶은 방장이며 개발자 입니다)

 

Comments