获取RecyclerView滚动距离的几种方法如下
第一种 computeVerticalScrollOffset方法
var dy = recycler.computeVerticalScrollOffset()
当RecyclerView的每个item高度都相同时,能正确的获取到滚动的距离dy,但是当每个item高度不一致时,获取到的值是不正确的,具体原因可以看computeVerticalScrollOffset方法的源码。
第二种 通过OnScrollListene监听累加
recycler.addOnScrollListener(object : RecyclerView.OnScrollListener(){
var mDy = 0
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
mDy += dy
}
})
这种方法在一般情况下也能正确的获得滚动的距离dy,但是当RecyclerView的item有变多或变少但是不触发OnScrollListener的时候,累加的mDy就会有误差。例如:使用adapter.notifyItemInserted(0)和adapter.notifyItemRemoved(0)方法
第三种 继承LayoutManager自己计算
GridLayoutManager如下
class OffsetGridLayoutManager(
context: Context?,
spanCount: Int,
orientation: Int,
reverseLayout: Boolean
) : GridLayoutManager(context, spanCount, orientation, reverseLayout) {
private val heightMap = HashMap<Int, Int>()
override fun onLayoutCompleted(state: RecyclerView.State?) {
super.onLayoutCompleted(state)
val firstVisible = findFirstVisibleItemPosition()
val lastVisible = findLastVisibleItemPosition()
if (firstVisible >= 0 && lastVisible >= 0 && lastVisible >= firstVisible) {
for (i in firstVisible until lastVisible) {
getChildAt(i)?.let {
val layoutParams = it.layoutParams as LayoutParams
if (layoutParams.spanIndex == 0) { //每行的最左边一个,一行只累加一个高度
heightMap[i] = it.height
} else {
heightMap[i] = 0
}
}
}
}
}
override fun computeVerticalScrollOffset(state: RecyclerView.State): Int {
if (childCount == 0) return 0
return try {
val firstVisiblePosition = findFirstVisibleItemPosition()
val viewByPosition = findViewByPosition(firstVisiblePosition)
var offset = 0
for (i in 0 until firstVisiblePosition) {
offset += heightMap[i] ?: 0
}
offset -= viewByPosition?.top ?: 0
offset
} catch (e: Exception) {
0
}
}
}
重写onLayoutCompleted方法并保存每一个View的高度(一行有多列的时候,只保存第一列view的高度,其他view的高度记录为0,因为不管一行有几列,行的高度都只会有一个view高度)
然后重写computeVerticalScrollOffset方法,计算高度。取得当前第一个可见的view的position,将该view前面所有view的高度累加,再加上该view向上滚动的距离,就是最终的滚动距离
同理LinearLayoutManager的话修改onLayoutCompleted就可以
class OffsetLinearLayoutManager(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : LinearLayoutManager(context, attrs, defStyleAttr, defStyleRes) {
private val heightMap = HashMap<Int, Int>()
override fun onLayoutCompleted(state: RecyclerView.State?) {
super.onLayoutCompleted(state)
val firstVisible = findFirstVisibleItemPosition()
val lastVisible = findLastVisibleItemPosition()
if (firstVisible >= 0 && lastVisible >= 0 && lastVisible >= firstVisible) {
for (i in firstVisible until lastVisible) {
getChildAt(i)?.let {
heightMap[i] = it.height
}
}
}
}
override fun computeVerticalScrollOffset(state: RecyclerView.State): Int {
if (childCount == 0) return 0
return try {
val firstVisiblePosition = findFirstVisibleItemPosition()
val viewByPosition = findViewByPosition(firstVisiblePosition)
var offset = 0
for (i in 0 until firstVisiblePosition) {
offset += heightMap[i] ?: 0
}
offset -= viewByPosition?.top ?: 0
offset
} catch (e: Exception) {
0
}
}
}