If we see the google play store android app, we can see that the app list in horizontal recycler view hold a property that the first property hold always full visible or not visible, but a portion visible is not seeing.
This property is snap property. In this article I show you how to use it in our own application. Basically we use two type of snap. Center Snap and start snap. I show you both here. So read full article here.
The Key Moment code is here-
For center snap you need to write 2 lines code-
//center Snap val snapHelper = LinearSnapHelper() snapHelper.attachToRecyclerView(recyclerView1)
For start snap, create a class in kotlin say StartSnapHelper.kt
package com.example.snaphelpersampleapp import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.OrientationHelper import androidx.recyclerview.widget.RecyclerView class StartSnapHelper : LinearSnapHelper() { private var mVerticalHelper: OrientationHelper? = null private var mHorizontalHelper: OrientationHelper? = null @Throws(IllegalStateException::class) override fun attachToRecyclerView(recyclerView: RecyclerView?) { super.attachToRecyclerView(recyclerView) } override fun calculateDistanceToFinalSnap( layoutManager: RecyclerView.LayoutManager, targetView: View ): IntArray? { val out = IntArray(2) if (layoutManager.canScrollHorizontally()) { out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager)) } else { out[0] = 0 } if (layoutManager.canScrollVertically()) { out[1] = distanceToStart(targetView, getVerticalHelper(layoutManager)) } else { out[1] = 0 } return out } override fun findSnapView(layoutManager: RecyclerView.LayoutManager): View? { return if (layoutManager is LinearLayoutManager) { if (layoutManager.canScrollHorizontally()) { getStartView(layoutManager, getHorizontalHelper(layoutManager)) } else { getStartView(layoutManager, getVerticalHelper(layoutManager)) } } else super.findSnapView(layoutManager) } private fun distanceToStart(targetView: View, helper: OrientationHelper?): Int { return helper!!.getDecoratedStart(targetView) - helper.startAfterPadding } private fun getStartView( layoutManager: RecyclerView.LayoutManager, helper: OrientationHelper? ): View? { if (layoutManager is LinearLayoutManager) { val firstChild = layoutManager.findFirstVisibleItemPosition() val isLastItem = (layoutManager .findLastCompletelyVisibleItemPosition() == layoutManager.getItemCount() - 1) if (firstChild == RecyclerView.NO_POSITION || isLastItem) { return null } val child = layoutManager.findViewByPosition(firstChild) return if (helper!!.getDecoratedEnd(child) >= helper.getDecoratedMeasurement(child) / 2 && helper.getDecoratedEnd(child) > 0 ) { child } else { if (layoutManager.findLastCompletelyVisibleItemPosition() == layoutManager.getItemCount() - 1 ) { null } else { layoutManager.findViewByPosition(firstChild + 1) } } } return super.findSnapView(layoutManager) } private fun getVerticalHelper(layoutManager: RecyclerView.LayoutManager): OrientationHelper? { if (mVerticalHelper == null) { mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager) } return mVerticalHelper } private fun getHorizontalHelper(layoutManager: RecyclerView.LayoutManager): OrientationHelper? { if (mHorizontalHelper == null) { mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager) } return mHorizontalHelper } }
Now in your activity-
//start snap val startSnapHelper = StartSnapHelper() startSnapHelper.attachToRecyclerView(recyclerView2)
Full Project Code-
activity_main.xml
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/tvCenterSnap" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="Center Snap" android:layout_marginTop="135dp" android:textSize="16sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintTop_toBottomOf="@id/tvCenterSnap" tools:listitem="@layout/rowview_snap" /> <TextView android:id="@+id/tvStartSnap" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="Start Snap" android:textSize="16sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@+id/recyclerView1" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView2" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintTop_toBottomOf="@id/tvStartSnap" tools:listitem="@layout/rowview_snap" /> </androidx.constraintlayout.widget.ConstraintLayout>
rowview_snap.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="260dp" android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" android:minHeight="160dp" android:background="@drawable/bg_rowview" android:layout_margin="5dp"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" android:text="Item 1" android:textSize="22sp" android:textColor="@color/white" android:textStyle="bold"/> </androidx.constraintlayout.widget.ConstraintLayout>
bg_rowview.xml in drawable-
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@color/teal_700" /> <stroke android:width="1dp" android:color="#C3636363" /> <corners android:radius="10dp" /> </shape>
TestAdapter.kt
package com.example.snaphelpersampleapp import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.rowview_snap.view.* class TestAdapter(private val list: ArrayList<String>) : RecyclerView.Adapter<TestAdapter.ViewHolder>() { //this method is returning the view for each item in the list override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val v = LayoutInflater.from(parent.context) .inflate(R.layout.rowview_snap, parent, false) return ViewHolder(v) } //this method is binding the data on the list override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bindItems(list[position], position) } //this method is giving the size of the list override fun getItemCount(): Int { return list.size } //the class is hodling the list view class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { @SuppressLint("SetTextI18n") fun bindItems(item: String, position: Int) { itemView.textView.text = "Item $position" } } }
MainActivity.kt
package com.example.snaphelpersampleapp import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSnapHelper import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { private val context = this private val list1 = ArrayList<String>() private val adapter1 = TestAdapter(list1) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) recyclerView1.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) recyclerView1.adapter = adapter1 recyclerView2.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) recyclerView2.adapter = adapter1 //center Snap val snapHelper = LinearSnapHelper() snapHelper.attachToRecyclerView(recyclerView1) //start snap val startSnapHelper = StartSnapHelper() startSnapHelper.attachToRecyclerView(recyclerView2) //set Temp data for (x in 0 until 10) { list1.add("Item") } adapter1.notifyDataSetChanged() } }
Others Files-
strings.xml
<resources> <string name="app_name">Snap helper sample app</string> </resources>
themes.xml
<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> <style name="Theme.SnapHelperSampleApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <!-- Primary brand color. --> <item name="colorPrimary">@color/purple_500</item> <item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorOnPrimary">@color/white</item> <!-- Secondary brand color. --> <item name="colorSecondary">@color/teal_200</item> <item name="colorSecondaryVariant">@color/teal_700</item> <item name="colorOnSecondary">@color/black</item> <!-- Status bar color. --> <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> <!-- Customize your theme here. --> </style> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorAccent">@color/purple_500</item> <item name="colorPrimary">@color/purple_500</item> <item name="colorPrimaryDark">@color/purple_500</item> </style> </resources>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.snaphelpersampleapp"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
See the output in this video-
Please follow me on blog.
Share this article
Thanks
Comments
Post a Comment