Search code examples
androidandroid-recyclerviewandroid-coordinatorlayoutandroid-nestedscrollview

android-add moving header above RecyclerView


I need to add a header above the RecyclerView in my app. the header must move as the user scrolls the recyclerview (right figure). I have already searched but have not found a final straightforward solution. what is the best and easiest way to implement it?

enter image description here

image credit: link


Solution

  • For this you have 2 alternatives.

    1. The first and simplest, you can add an ImageView in a CollapsingToolbarLayout inside a CoordinatorLayout and then add app:layout_behavior="@string/appbar_scrolling_view_behavior" to your RecyclerView

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        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="match_parent">
    
            <com.google.android.material.appbar.AppBarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content>
    
                <com.google.android.material.appbar.CollapsingToolbarLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:fitsSystemWindows="true"
                    app:layout_scrollFlags="scroll|exitUntilCollapsed">
    
                        <ImageView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"/>
    
                        <androidx.appcompat.widget.Toolbar
                            android:layout_width="match_parent"
                            android:layout_height="?actionBarSize"
                            app:layout_collapseMode="pin"/>
    
                </com.google.android.material.appbar.CollapsingToolbarLayout>
    
            </com.google.android.material.appbar.AppBarLayout>
    
            <androidx.recyclerview.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/transparent"
                android:fillViewport="true"
                app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    
    1. The second option which will have the same effect when scrolling, is that in your ReyclerViewAdapter it understands 2 viewTypes, one for the image and the other for the items, in this way you must pass the image as the first item of your item list
    class CustomAdapter(
                private var list: ArrayList<String>
        ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    
    
            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
                return if (viewType == VIEW_TYPE_ONE) {
                    ViewHolder1(LayoutInflater.from(context).inflate(R.layout.your_list_item_1, parent, false))
                } else ViewHolder2(LayoutInflater.from(context).inflate(R.layout.your_list_item_2, parent, false))
            }
    
            override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
                if (position == 0) {
                    (holder as ViewHolder1).bind(position)
                } else {
                    (holder as ViewHolder2).bind(position)
                }
            }
    
            override fun getItemCount(): Int {
                return list.size
            }
    
            override fun getItemViewType(position: Int): Int {
                return if (position == 0) {
                    VIEW_TYPE_ONE
                } else VIEW_TYPE_TWO
            }
    
    
            private inner class ViewHolder1 internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
    
                var yourView: ImageView  = itemView.findViewById(R.id.yourView)
    
                fun bind(position: Int) {
                    yourView.setImageResource(list[position])
                }
            }
    
            private inner class ViewHolder2 internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
    
                var yourView: TextView = itemView.findViewById(R.id.yourView)
    
                fun bind(position: Int) {
                    yourView.text = list[position]
                }
            }
    
            companion object {
                private const val VIEW_TYPE_ONE = 1
                private const val VIEW_TYPE_TWO = 2
            }