-
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
나는 이 분 소스를 많이 참고 했다..
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
'Dev > android' 카테고리의 다른 글
Sticky Header Recyclerview using ItemDecoration without library - 2 (Kotlin) (3) 2020.05.27 Glide로 서버에서 이미지를 받아 MenuItem의 아이콘 변경하기 (비트맵으로 변경하기) (0) 2020.01.14 ImageView에서 background 와 src 의 차이 (0) 2019.12.16 OS버전 별 변경점 (0) 2019.08.16 안드로이드 웹뷰에서 로그인 세션 유지하기 (1) 2019.05.09