ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Sticky Header Recyclerview using ItemDecoration without library - 1 (Kotlin)
    Dev/android 2020. 5. 26. 22:56

    Sticky Header 란? 사용자가 페이지를 아래로 스크롤 할 때, 화면 상단에 고정된 뷰를 말한다.

     

    검색해 보면 라이브러리 한 줄로 사용하기도 쉽게 해뒀지만, 각자의 상황에 맞게 커스텀 하려면 라이브러리는 불편하다

     

    ItemDecoration을 커스텀 해서 고정할 헤더 뷰를 그리기로 했다. 아래는 예제 샘플이다.

     

    바쁜 사람은 여기 를 눌러 소스를 보러 가시길...

     

     

    아래 부턴 삽질 히스토리 

     

     

    사실 맨 위의 상단을 고정하는 건 검색해보면 되게 많이 나온다.

    1. 

    https://github.com/paetztm/recycler_view_headers

     

    paetztm/recycler_view_headers

    Simple Recycler View Section Header implementation - paetztm/recycler_view_headers

    github.com

    나는 이 분 소스를 많이 참고 했다..

     

    2. https://stackoverflow.com/questions/32949971/how-can-i-make-sticky-headers-in-recyclerview-without-external-lib

     

    How can I make sticky headers in RecyclerView? (Without external lib)

    I want to fix my header views in the top of the screen like in the image below and without using external libraries. In my case, I don't want to do it alphabetically. I have two different types of...

    stackoverflow.com

    하지만 내가 구현해야 할 상황은 고정 될 뷰 위에 함께 스크롤 되어야할 뷰가 하나 더 존재하는 상황이다.

    즉, 전체 RecyclerView에 고정되어야 할 뷰 인덱스가 0이 아니라 1인 경우이다.

     

    먼저 RecyclerView를 만든다.

     

    activity_main.xml

    <?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"
        tools:context=".MainActivity">
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"/>
    </LinearLayout>

     

    MainActivity.kt

    class MainActivity : AppCompatActivity() {
        private lateinit var adapter: MainAdapter
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            setupAdapter()
        }
    
        private val peopleInfoList = listOf(
            Data("apple", 17),
            Data("banana", 31),
            Data("car", 26),
            Data("dog", 26),
            Data("egg", 24),
            Data("fish", 33),
            Data("great", 40),
            Data("happy", 10),
            Data("ice", 20),
            Data("juice", 32),
            Data("key", 50),
            Data("lonnie", 30),
            Data("mom", 58),
            Data("notice", 11),
            Data("object", 36),
            Data("people", 38),
            Data("queen", 15),
            Data("right", 22),
            Data("sight", 27),
            Data("tiger", 13),
            Data("uv", 29),
            Data("virus", 44),
            Data("wow", 36),
            Data("xylitol", 24),
            Data("yes", 26),
            Data("zoo", 36)
        )
    
        private fun setupAdapter() {
            adapter = MainAdapter(peopleInfoList)
            val layoutManager = LinearLayoutManager(this)
            recycler.layoutManager = layoutManager
            recycler.adapter = adapter
        }
    }

     

    Data.kt

    data class Data(var name: String = "", var age: Int = -1)

    RecyclerView에 집어넣을 데이터 클래스를 만들고 RecyclerView와 Adapter를 연결 시킨다.

    Adapter엔 Data를 모델로 한 리스트를 매개 변수로 넣어준다.

     

    ItemViewType을 정해보자.

     

    최 상단에 올라갈 뷰를 TYPE_TOP 으로 정한다.

    그 다음 리스트에서 고정될 뷰는 TYPE_HOLDER 

    리스트가 그려질 뷰는 TYPE_ITEM

    리스트가 없으면 TYPE_EMPTY

     

        companion object {
            const val TYPE_TOP = 0
            const val TYPE_HOLDER = 1
            const val TYPE_EMPTY = 2
            const val TYPE_LIST = 3
        }
    
        override fun getItemViewType(position: Int): Int {
            return when (position) {
                0 -> TYPE_TOP
                1 -> TYPE_HOLDER
                2 -> TYPE_EMPTY
                else -> TYPE_LIST
            }
        }

    여기서 ViewType이 TYPE_HOLDER일 경우에 스크롤 시 상단에 그려진다는 것을 유의하자 TYPE_TOP과 혼돈 주의

    각 뷰에 해당하는 layout xml을 만들고 뷰홀더에 bind 해준다.

     

    top_item.xml

    <?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"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="1dp"
            android:background="#F98740"
            android:gravity="center"
            android:minHeight="54dp"
            android:paddingLeft="16dp"
            android:paddingTop="20dp"
            android:paddingRight="16dp"
            android:paddingBottom="20dp"
            android:text="This is a top type view"
            android:textSize="16sp"
            android:textStyle="bold" />
    </LinearLayout>

    hold_item.xml

    <?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"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="1dp"
            android:background="#ff8181"
            android:gravity="center"
            android:minHeight="54dp"
            android:paddingLeft="16dp"
            android:paddingTop="20dp"
            android:paddingRight="16dp"
            android:paddingBottom="20dp"
            android:text="This is a hold type view"
            android:textSize="16sp"
            android:textStyle="bold" />
    </LinearLayout>

    list_item.xml

    <?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"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:paddingTop="19dp"
            android:paddingBottom="19dp">
    
            <TextView
                android:id="@+id/tv_name"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="20dp"
                android:layout_weight="1"
                android:text="text"
                android:textSize="16sp" />
    
            <TextView
                android:id="@+id/tv_age"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="13dp"
                android:drawablePadding="10.3dp"
                android:text="30"
                android:textSize="14sp" />
    
        </LinearLayout>
    </LinearLayout>

    empty_item.xml

    <?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"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center|top"
            android:orientation="vertical">
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="26dp"
                android:gravity="center"
                android:text="There are currently no registered Item."
                android:textSize="16sp"
                android:textStyle="bold" />
        </LinearLayout>
    </LinearLayout>

    Adapter 내에서 각 View Type 에 맞게 뷰 홀더를 만들어 바인드 해준다.

        override fun onCreateViewHolder(parent: ViewGroup, position: Int): RecyclerView.ViewHolder {
            val view: View
            return when (recyclerItemList[position].type) {
                TYPE_TOP -> {
                    view = LayoutInflater.from(parent.context)
                        .inflate(R.layout.top_item, parent, false)
                    TopViewHolder(view)
                }
                TYPE_HOLDER -> {
                    view = LayoutInflater.from(parent.context)
                        .inflate(R.layout.hold_item, parent, false)
                    HolderViewHolder(view)
                }
                TYPE_EMPTY -> {
                    view = LayoutInflater.from(parent.context)
                        .inflate(R.layout.empty_item, parent, false)
                    EmptyViewHolder(view)
                }
                else -> {
                    view = LayoutInflater.from(parent.context)
                        .inflate(R.layout.list_item, parent, false)
                    ItemViewHolder(view)
                }
            }
    
        }
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            when (holder) {
                is TopViewHolder -> {
                    holder.bindView()
                }
                is HolderViewHolder -> {
                    holder.bindView()
                }
                is EmptyViewHolder -> {
                    holder.bindView()
                }
                is ItemViewHolder -> {
                    holder.bindView()
                }
            }
        }
    inner class TopViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            fun bindView() {
            }
        }
    
        inner class HolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            fun bindView() {
            }
        }
    
        inner class EmptyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            fun bindView() {
            }
        }
    
        inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            fun bindView() {
            }
        }

    다음 페이지에서 계속.. NEXT

Designed by Tistory.