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

时间:2019-12-16 05:20:30

标签: android kotlin android-recyclerview scroll androidx

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

我尝试过:

smoothScrollToPosition(targetPosition)

它确实进行了缓慢的滚动(我也可以通过扩展LinearLayoutManager并覆盖calculateSpeedPerPixel()来控制速度)(请参阅How can i control the scrolling speed of recyclerView.smoothScrollToPosition(position)),但是在此之后,我无法控制列表项在屏幕上的确切位置滚动。我所知道的是它将在屏幕上完全可见。

我尝试过:

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

它给我错误消息:“ W / RecyclerView:RecyclerView不支持滚动到绝对位置。请改用scrollToPosition”

我还尝试过用LinearLayoutManager替换RecyclerView,该对象包含所有项作为子项,并在视图中平移LinearLayoutManager,但最初没有让子项在屏幕外绘制。

这个问题有解决方案吗?

2 个答案:

答案 0 :(得分:1)

您可以根据实际目标位置和可见项目的数量来计算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)

答案 1 :(得分:0)

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

enter image description here