Search code examples
androidkotlinandroid-recyclerviewscrollandroidx

Scroll an Android RecyclerView and control the exact location on screen


I am looking for a way to programmatically slowly scroll a RecyclerView so as to bring a certain element targetPosition exactly in the middle of the screen. Note that all my items in the RecylerView have the same height by design. The RecyclerView is vertical. I am programming in Kotlin and AndroidX

I have tried:

smoothScrollToPosition(targetPosition)

It does the scrolling slowly (I can also control the speed by extending the LinearLayoutManager and overriding calculateSpeedPerPixel() - see How can i control the scrolling speed of recyclerView.smoothScrollToPosition(position)), however I can't control the exact location of the list item on the screen after the scrolling. All I know is it will be fully visible on the screen.

I have tried:

scrollToX(0, targetPosition * itemHeight - screenHeight / 2)

It gives me the error message: "W/RecyclerView: RecyclerView does not support scrolling to an absolute position. Use scrollToPosition instead"

I have also tried replacing the RecyclerView by a LinearLayoutManager which contains all the items as children, and translate the LinearLayoutManager in the view, but I don't get the children initially outside of the screen to draw.

Is there a solution to this issue?


Solution

  • You can calculate the smoothScrollToPosition's targetPosition based on your actual target position and the number of visible items.

    A quick POC on how to do that:

      val scrollToPosition = 50
    
      val layoutManager = recyclerView.layoutManager as LinearLayoutManager
      val firstPosition = layoutManager.findFirstVisibleItemPosition()
      val lastPosition = layoutManager.findLastVisibleItemPosition()
      val visibleItems =  lastPosition - firstPosition + 1
    
      if (firstPosition < scrollToPosition) {
          recyclerView.smoothScrollToPosition(scrollToPosition + (visibleItems / 2))
      } else {
          recyclerView.smoothScrollToPosition(scrollToPosition - (visibleItems / 2))
      }
    

    If you want more precise results that the item should be exactly at the middle of the screen, you can use the heights of the item (since its fixed) and the height of the RecyclerView, then calculate the offset to scroll. And call:

    recyclerView.scrollBy(dx, dy)
    

    Or:

    recyclerView.smoothScrollBy(dx, dy)