In this blog, we are going to learn about how to scroll RecyclerView to show the selected item on top. We will cover the Android scrolling flow part in this blog. So let’s get started.
An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event has occurred on that RecyclerView.
When the recycler views vertically scrolls, then the selected item is centered or on the Top First item, and then the item is automatically centered or to be the first when the item to be selected is set.
Let’s understand this with an example.
1.) At the very first, we have to create the one activity class in which the recycler is a vertical orientation.
An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event has occurred on that RecyclerView.
We have a RecyclerView
list, in which I want the currently selected item to be shown at the top of the RecyclerView
. I still, however, want the entire list to be scrollable, therefore, removing views from above the selected item is not a possible solution.
We create the XML part of the MainActivity UI part named activity_main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="visible"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/constraint_multiple_options" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <com.google.android.material.tabs.TabLayout android:id="@+id/tabs" style="@style/MyCustomTabLayout" android:layout_width="0dp" android:layout_height="60.4dp" android:layout_marginTop="10dp" android:elevation="5dp" android:gravity="start" android:tabStripEnabled="false" android:textAlignment="viewStart" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:tabGravity="fill" app:tabIndicatorColor="@android:color/transparent" app:tabIndicatorHeight="0dp" app:tabMode="scrollable" app:tabTextAppearance="@style/MyCustomTextAppearance" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/linear_recycler_menuItem" android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginTop="12dp" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tabs" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout> <LinearLayout android:id="@+id/mTransparent" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/transparent_color" android:orientation="vertical" android:visibility="gone" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentBottom="true" android:layout_marginTop="50dp" android:background="@android:color/transparent" android:orientation="vertical" android:paddingTop="50dp"> <androidx.cardview.widget.CardView android:id="@+id/card_menu_list" android:layout_width="252dp" android:layout_height="wrap_content" android:layout_above="@id/linear_menubtn" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_marginEnd="19dp" android:layout_marginRight="19dp" android:layout_marginBottom="@dimen/margin_20" android:elevation="10dp" android:visibility="gone" app:cardBackgroundColor="@color/white" app:cardCornerRadius="10dp"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/menuListing" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginHorizontal="20dp" android:layout_marginTop="15dp" android:layout_marginBottom="15dp" android:gravity="start" android:textAlignment="viewStart" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.cardview.widget.CardView> <LinearLayout android:id="@+id/linear_menubtn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:orientation="vertical"> <Button android:id="@+id/menu_btn" android:layout_width="105.6dp" android:layout_height="45.9dp" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_gravity="end" android:layout_marginEnd="18dp" android:layout_marginRight="18dp" android:layout_marginBottom="18dp" android:background="@drawable/rounded_all_side_pink" android:padding="10dp" android:text="Menu" android:textColor="@color/white" /> </LinearLayout> </RelativeLayout> </RelativeLayout> </RelativeLayout> |
In the styles, “MyCustomTabLayout” you can add it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<style name="MyCustomTabLayout" parent="Widget.Design.TabLayout"> <item name="tabIndicatorHeight">0dp</item> <item name="tabPaddingStart">20dp</item> <item name="tabPaddingEnd">20dp</item> <!-- <item name="tabBackground">@drawable/tab_color_selector</item>--> <item name="tabTextAppearance">@style/MyCustomTextAppearance</item> <item name="tabSelectedTextColor">@color/white</item> </style> <style name="MyCustomTextAppearance" parent="TextAppearance.Design.Tab"> <item name="textAllCaps">false</item> <item name="android:textSize">16sp</item> <item name="android:color">@color/black</item> <item name="singleLine">true</item> <item name="android:textColor">@color/black</item> </style> |
In the Colors, you can add them below.
1 2 3 4 |
<color name="drawer_bg">#FEF8FB</color> <color name="white">#FFFFFF</color> <color name="black">#000000</color> <color name="transparent_color">#66000000</color> |
You can create in Drawable, an XML file named “rounded_all_side_pink”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <!-- we define background color --> <solid android:color="@color/colorPrimary" /> <!-- we define border color and thick --> <stroke android:width="2dp" android:color="@color/colorPrimary" /> <!-- we define corner radius, note that radius can be different for each corner --> <corners android:radius="10dp" /> </shape> |
2.) The Scrolling view can be performed by using the onScrollListener()
We take only dummy data for showing the items as in 20 dummy data in the ArrayList.
The callback method is to be invoked when the RecyclerView has been scrolled. This will be called after the scroll has been completed.
This callback will also be called if the visible item range changes after a layout calculation. In that case, dx and dy will be 0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
class RecyclerListingActivity : AppCompatActivity() { private var responseItemList: MutableList<String>? = ArrayList() private var linearLayoutManager: LinearLayoutManager? = null private var menuListAdapter: MenuListAdapter? = null private var dailyCakeListScrollAdapter: DailyCakeListScrollAdapter? = null private var flagCheck = false private var checkCallBack = false private var checkScroll = true private var posSelected = 0 private var totalCount = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_recycler_listing) setData() } private fun setData() { responseItemList?.add("test1") responseItemList?.add("test2") responseItemList?.add("test3") responseItemList?.add("test4") responseItemList?.add("test5") responseItemList?.add("test6") responseItemList?.add("test7") responseItemList?.add("test8") responseItemList?.add("test9") responseItemList?.add("test10") responseItemList?.add("test11") responseItemList?.add("test12") responseItemList?.add("test13") responseItemList?.add("test14") responseItemList?.add("test15") responseItemList?.add("test16") responseItemList?.add("test17") responseItemList?.add("test18") responseItemList?.add("test19") responseItemList?.add("test20") linearLayoutManager = LinearLayoutManager(this@RecyclerListingActivity, LinearLayoutManager.VERTICAL, false) linear_recycler_menuItem.layoutManager = linearLayoutManager menuListing.layoutManager = LinearLayoutManager( this@RecyclerListingActivity, LinearLayoutManager.VERTICAL, false ) setTabs() } private fun setTabs() { for (aq in responseItemList!!.indices) { tabs!!.addTab(tabs!!.newTab().setText(responseItemList!![aq])) tabs!!.tabGravity = TabLayout.GRAVITY_FILL } setAdapter() } private fun setAdapter() { setMenuAdapter() initCategoryMenu() } fun bindWidgetsWithAnEvent() { tabs!!.addOnTabSelectedListener(object : OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab) { checkCallBack = true posSelected = tab.position linear_recycler_menuItem!!.smoothScrollToPosition(tab.position) tabs!!.setSelectedTabIndicatorHeight((2 * resources.displayMetrics.density).toInt()) selectionBackground(posSelected) if (tab.position == totalCount - 1) { checkCallBack = false } } override fun onTabUnselected(tab: TabLayout.Tab) { checkCallBack = false } override fun onTabReselected(tab: TabLayout.Tab) { checkCallBack = true posSelected = tab.position selectionBackground(posSelected) linear_recycler_menuItem!!.smoothScrollToPosition(tab.position) tabs!!.setSelectedTabIndicatorHeight((2 * resources.displayMetrics.density).toInt()) selectionBackground(posSelected) if (tab.position == totalCount - 1) { checkCallBack = false } } }) } private fun initCategoryMenu() { dailyCakeListScrollAdapter = DailyCakeListScrollAdapter(responseItemList!!) linear_recycler_menuItem!!.adapter = dailyCakeListScrollAdapter setScrollListener() bindWidgetsWithAnEvent() } private fun setScrollListener() { checkScroll = true linear_recycler_menuItem!!.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) val visiblePosition = linearLayoutManager!!.findFirstVisibleItemPosition() val firstCompletelyVisiblePosition = linearLayoutManager!!.findFirstCompletelyVisibleItemPosition() totalCount = linearLayoutManager!!.itemCount checkScroll = false if (visiblePosition > -1) { if (!checkCallBack) { if (firstCompletelyVisiblePosition == -1) { selectionBackground(visiblePosition) tabs!!.setScrollPosition(visiblePosition, 0f, true) } else { selectionBackground(firstCompletelyVisiblePosition) tabs!!.setScrollPosition(firstCompletelyVisiblePosition, 0f, true) } } else { selectionBackground(posSelected) tabs!!.setScrollPosition(posSelected, 0f, true) if (firstCompletelyVisiblePosition == posSelected) { checkCallBack = false } } } } }) } private fun selectionBackground(position: Int) { for (i in responseItemList!!.indices) { if (i == position) { val tabLayout = (tabs!!.getChildAt(0) as ViewGroup).getChildAt(position) as LinearLayout val tabTextView = tabLayout.getChildAt(1) as TextView if (tabTextView != null) { tabTextView.setTextColor(Color.parseColor("#ffffff")) tabTextView.setPadding(11, 7, 11, 7) tabTextView.setBackgroundResource(R.drawable.rounded_all_side_pink) } val tab = (tabs!!.getChildAt(0) as ViewGroup).getChildAt(position) val p = tab.layoutParams as MarginLayoutParams p.setMargins(0, 0, 0, 0) tab.requestLayout() tabs!!.isSmoothScrollingEnabled = true tabs!!.setScrollPosition(position, 0f, true) } else { val tabLayout = (tabs!!.getChildAt(0) as ViewGroup).getChildAt(i) as LinearLayout val tabTextView = tabLayout.getChildAt(1) as TextView if (tabTextView != null) { tabTextView.setPadding(11, 7, 11, 7) tabTextView.setTextColor(Color.parseColor("#000000")) tabTextView.setBackgroundResource(R.drawable.rounded_all_side_transparent) } val tab = (tabs!!.getChildAt(0) as ViewGroup).getChildAt(i) val p = tab.layoutParams as MarginLayoutParams p.setMargins(0, 0, 0, 0) tab.requestLayout() } } } private fun setMenuAdapter() { menuListAdapter = MenuListAdapter( responseItemList!!, object : MenuListAdapter.OnItemClickListener { override fun onItemClick(position: Int, check: Boolean) { checkCallBack = check posSelected = position mTransparent!!.visibility = View.GONE card_menu_list!!.visibility = View.GONE flagCheck = false tabs!!.isSmoothScrollingEnabled = true selectionBackground(position) tabs!!.setScrollPosition(position, 0f, true) linear_recycler_menuItem!!.smoothScrollToPosition(position) } }) menuListing!!.adapter = menuListAdapter menu_btn!!.setOnClickListener { if (!flagCheck) { checkCallBack = false mTransparent!!.visibility = View.VISIBLE card_menu_list!!.visibility = View.VISIBLE flagCheck = true } else { checkCallBack = false mTransparent!!.visibility = View.GONE card_menu_list!!.visibility = View.GONE flagCheck = false } } } } |
Here, An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event has occurred on that RecyclerView.
We added one TabLayout under which the items get the scroll, and below the tab’s layout, there is Recyclerview in which all items get scrolled vertically.
3.) After that for implementing the Recyclerview, we add the Adapter.
Here we implement the home screen recycler view adapter named “DailyCakeListScrollAdapter” and for the menu item.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class DailyCakeListScrollAdapter( var categoryMenuItemList1: List<String> ) : RecyclerView.Adapter<FirstViewHolderr>() { override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): FirstViewHolderr { val v = LayoutInflater.from(viewGroup.context) .inflate(R.layout.layout_menu_list, viewGroup, false) return FirstViewHolderr(v) } override fun onBindViewHolder(firstViewHolder: FirstViewHolderr, i: Int) { firstViewHolder.itemView.text_cake.text = categoryMenuItemList1[i] firstViewHolder.itemView.textCount.text = "Pizza " + i + 1 } override fun getItemCount(): Int { return categoryMenuItemList1.size } inner class FirstViewHolderr(itemView: View) : RecyclerView.ViewHolder(itemView) } |
4.) Notify the XML part of this adapter named “layout_menu_list”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_marginHorizontal="3dp" android:layout_marginBottom="10dp"> <androidx.cardview.widget.CardView android:id="@+id/card_image" android:layout_width="105sp" android:layout_height="105sp" app:cardCornerRadius="10dp" android:layout_margin="3dp" app:cardElevation="5dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/image_cake" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="start" android:scaleType="centerCrop" android:src="@drawable/ic_launcher_background" android:textAlignment="viewStart" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.cardview.widget.CardView> <TextView android:id="@+id/text_cake" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="7.8dp" android:layout_marginLeft="7.8dp" android:layout_marginTop="5dp" android:gravity="start" android:text="Pizza Hut" android:textAlignment="viewStart" android:textColor="@color/black" android:textSize="17sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/card_image" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textCount" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="7.8dp" android:layout_marginEnd="14dp" android:layout_marginTop="4dp" android:paddingBottom="5dp" android:gravity="start" android:minLines="2" android:maxLines="4" android:text="Pizza" android:textAlignment="viewStart" android:textColor="@color/black" android:textSize="14sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/card_image" app:layout_constraintTop_toBottomOf="@id/text_cake" app:layout_constraintVertical_bias="0.0" android:layout_marginLeft="7.8dp" android:layout_marginRight="14dp" /> <TextView android:id="@+id/text_cakePrice" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="6dp" android:layout_marginVertical="8dp" android:layout_marginEnd="14dp" android:gravity="start" android:textAlignment="viewStart" android:textColor="@color/black" android:text="20.00" android:textSize="15sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@id/textCount" android:layout_marginLeft="6dp" android:layout_marginRight="14dp" /> <TextView android:id="@+id/text_cakeOfferPrice" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="6dp" android:layout_marginVertical="8dp" android:layout_marginEnd="14dp" android:gravity="start" android:text="price" android:textAlignment="viewStart" android:textColor="@color/black" android:textSize="15sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/text_cakePrice" app:layout_constraintTop_toBottomOf="@id/textCount" android:layout_marginLeft="6dp" android:layout_marginRight="14dp" /> </androidx.constraintlayout.widget.ConstraintLayout> |
5.) For the menu, item listing add the recycler adapter for the “MenuListAdapter”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
class MenuListAdapter( private val categoryMenuItemList: List<String>, private val listener: OnItemClickListener ) : RecyclerView.Adapter<MenuListAdapter.ViewHolder>() { var flag = false var positionSelected = 0 interface OnItemClickListener { fun onItemClick(position: Int, check: Boolean) } override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder { val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.item_menu, viewGroup, false) return ViewHolder(v) } override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { viewHolder.itemView.menu_name.text = categoryMenuItemList[position] flag = if (positionSelected == position) { viewHolder.itemView.menu_name.setTextColor(Color.parseColor("#D1007E")) viewHolder.itemView.text_menu_counts.setTextColor(Color.parseColor("#D1007E")) false } else { viewHolder.itemView.menu_name.setTextColor(Color.parseColor("#000000")) viewHolder.itemView.text_menu_counts.setTextColor(Color.parseColor("#000000")) true } } override fun getItemCount(): Int { return categoryMenuItemList.size } inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { init { itemView.setOnClickListener { positionSelected = adapterPosition notifyDataSetChanged() listener.onItemClick(positionSelected, true) } } } } |
You can refer to this screenshot for the menu list view in the application. How it looks like
6.) The last UI of the mentioned Menu screen is added below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="20dp" xmlns:app="http://schemas.android.com/apk/res-auto"> <TextView android:id="@+id/menu_name" android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="start" android:textAlignment="viewStart" android:text="" android:textColor="@color/black" android:textSize="17sp" app:layout_constraintEnd_toStartOf="@id/text_menu_counts" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/text_menu_counts" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="start" android:text="" android:textColor="@color/black" android:textAlignment="viewStart" android:textSize="17sp" android:focusable="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toEndOf="@id/menu_name" app:layout_constraintTop_toTopOf="@id/menu_name" app:layout_constraintBottom_toBottomOf="@id/menu_name"/> </androidx.constraintlayout.widget.ConstraintLayout> |
Finally, we implemented the code part of the scrolling view Recycler in this blog.
Also, we use the base class for smooth scrolling. Handles basic tracking of the target view position and provides methods to trigger a programmatic scroll.
An instance of SmoothScroller is only intended to be used once. You should create a new instance for each call to LayoutManager#startSmoothScroll(SmoothScroller)
You can also check these links.
https://mobikul.com/adding-fast-scroller-recyclerview/
Another mentioned URL
I hope this blog will help you to how to Scroll RecyclerView to show the selected items on top.
For more understanding please can go through this link. That’s all, You can enjoy your recycler view scrolling feature. Thank you very much.