滚动 Android RecyclerView 并控制屏幕上的确切位置

Scroll an Android RecyclerView and control the exact location on screen

我正在寻找一种以编程方式缓慢滚动 RecyclerView 以便将某个元素 targetPosition 恰好放在屏幕中间的方法。请注意,我在 RecylerView 中的所有项目在设计上都具有相同的高度。 RecyclerView 是垂直的。我正在使用 Kotlin 和 AndroidX

编程

我试过:

smoothScrollToPosition(targetPosition)

它滚动缓慢(我也可以通过扩展 LinearLayoutManager 和覆盖 calculateSpeedPerPixel() 来控制速度 - 参见 ),但是我无法控制列表项在滚动后的屏幕。我所知道的是它将在屏幕上完全可见。

我试过:

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

它给我错误信息:"W/RecyclerView: RecyclerView does not support scrolling to an absolute position. Use scrollToPosition instead"

我也曾尝试用包含所有子项的 LinearLayoutManager 替换 RecyclerView,并在视图中转换 LinearLayoutManager,但最初我没有让子项在屏幕外绘制。

这个问题有解决办法吗?

您可以根据您的实际目标位置和可见项的数量来计算smoothScrollToPosition的targetPosition。

关于如何做到这一点的快速 POC:

  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))
  }

如果你想要更精确的结果,即项目应该正好在屏幕中间,你可以使用项目的高度(因为它是固定的)和 RecyclerView 的高度,然后计算偏移量以滚动.并调用:

recyclerView.scrollBy(dx, dy)

或者:

recyclerView.smoothScrollBy(dx, dy)

谢谢@Bob。我错过了 scrollBy()。按照您的建议,这是对我有用的代码。

class RecyclerViewFixedItemSize : RecyclerView {
    var itemFixedSize : Int = 0

    constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) {
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
    }

    constructor(context: Context) : super(context) {
    }

    fun smoothScrollToPositionCentered(position: Int) {
        // Find the fixed item size - once for all
        if (itemFixedSize == 0) {
            val ll = layoutManager as LinearLayoutManager
            val fp = ll.findFirstCompletelyVisibleItemPosition()
            val fv = ll.getChildAt(fp)
            itemFixedSize = fv!!.height
        }

        // find the recycler view position and screen coordinates of the first item (partially) visible on screen
        val ll = layoutManager as LinearLayoutManagerFixedItemSize
        val fp = ll.findFirstVisibleItemPosition()
        val fv = ll.getChildAt(0)

        // find the middle of the recycler view
        val dyrv = (top - bottom ) / 2

        // compute the number of pixels to scroll to get the view position in the center
        val dy = (position - fp) * itemFixedSize + dyrv + fv!!.top + itemFixedSize / 2

        smoothScrollBy(0, dy)
    }
}