How to make a Recycler View scroll slow and smooth when the user takes hands off the screen?

Viewed 282

This is how the current implementation when I scroll fast.

enter image description here

With in second the whole items in the recycler view are showed till the end.. What I am trying to achieve is something like the one shown below (live demo: recycler views in play store),

enter image description here

even if we scroll fast it shows 1 or 2 items so it feels natural. How can I make the recycler view behave this way?

The code:

Adapter

class CustomAdapter(private val mList: List<ItemsViewModel>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.card_view_design, parent, false)

        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {

    }

    override fun getItemCount(): Int {
        return mList.size
    }

    class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
        val imageView: ImageView = itemView.findViewById(R.id.imageview)
        val textView: TextView = itemView.findViewById(R.id.textView)
    }
}

Implementation

val recyclerview = findViewById<RecyclerView>(R.id.recyclerview)
recyclerview.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
val data = ArrayList<ItemsViewModel>()
val adapter = CustomAdapter(data)
recyclerview.adapter = adapter
1 Answers

When a RecyclerView is scrolled horizontally, the scrollHorizontallyBy() is called several times with dx values that can make the scrolling behave smooth and real according to the fling (push) value of the finger.

The same for vertical RecyclerView, scrollVerticallyBy() gets called with different dy values.

So, you can manipulate this dx value to have the desired behavior; in your case you need to get smaller values of dx.

You'd think of a fixed dx value, but that won't be that natural; instead you can divide it by a fixed amount to scale down the same dx values.

Here I'm using an arbitrary value that you can manipulate it as you need.

So, what you need is to use a custom LinearLayoutManager & override scrollHorizontallyBy() as you use a horizontal recyclerView:

class MyLinearLayoutManager(context: Context?, orientation: Int, reverseLayout: Boolean) :
    LinearLayoutManager(context, orientation, reverseLayout) {

    override fun scrollHorizontallyBy(
        dx: Int,
        recycler: RecyclerView.Recycler?,
        state: RecyclerView.State?
    ): Int {
        var newDx = (dx / 1.2).toInt() // 1.2 is an arbitrary value 
        if (newDx == 0 && dx != 0) { // To have no 0 values (Optionally)
            newDx = 1
        }
        
        return super.scrollHorizontallyBy(newDx, recycler, state)
    }

}
Related