获取 RecyclerView 当前屏幕中可见 item 下标
我们只需要设置对 RecyclerView 设置滑动监听即可(当 RecyclerView 停下滑动时进行检测):
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
emitVisibleItems()
}
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dx == 0 && dy == 0) {
emitVisibleItems()
}
}
fun emitVisibleItems() {
val manager = recyclerView.layoutManager
if (manager is LinearLayoutManager) {
val firstPosition = manager.findFirstVisibleItemPosition()
val lastPosition = manager.findLastVisibleItemPosition()
val visibleRange = mutableListOf<Int>()
for (i in firstPosition..lastPosition) {
val view = manager.findViewByPosition(i) ?: continue
val rect = Rect()
val isVisible = view.getGlobalVisibleRect(rect)
if (isVisible) {
visibleRange.add(i)
}
}
// iVisualItems 是 VisibleItemsListener 类型,回调接口
iVisualItems.onItemsVisible(visibleRange)
}
}
interface VisibleItemsListener {
fun onItemsVisible(items: List<Int>)
}
这里是 LinearLayoutManager 为例,其他 Manager 也是类似,都有类似的 findFirstVisibleItemPosition() 和 findLastVisibleItemPosition() 方法。
*** 还有类似的findFirstCompletelyVisiblePosition() 和 findLastCompletelyVisiblePosition() 方法可供使用,当然意义是不同的** ****
判断 RecyclerView 某个 View 是否完全显示
/**
* 调用此方法最好延时 16ms (即一帧)
*/
fun isCompletelyVisible(): Boolean {
val rect = Rect()
val isVisible = view.getGlobalVisibleRect(rect)
return isVisible && (rect.bottom - rect.top >= view.height)
}
实际上这个方法应该可以用于判断任何 View,只不过在 RecyclerView 中更常见。为啥需要总结这个呢,因为出于最近业务上的一个需求:
如图所示,这是 RecyclerView 中的一个 itemView,原本的逻辑是整项滑出后,播放器停止播放(这里我们可以通过对播放器 View 设置addOnAttachStateChangeListener(),然后在其onViewDetachedFromWindow()回调方法里进行处理)。有一天产品改了逻辑:只要播放器 View 不是完全可见,就停止播放,当然原来的逻辑也需要保留,因为需要防止滑动过快时视频无法自动停止。这里我们就可以综合 1,2两点,通过 RecyclerView 的局部刷新方法进行联系即可实现功能,示例代码如下:
const val ITEM_MOST_VISIBLE = 1
override fun onItemsVisible(items: List<Int>) {
items.forEach{ position ->
adapter.notifyItemChanged(position, ITEM_MOST_VISIBLE)
}
}
@Override
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position, @NonNull List<Object> payloads) {
if (!payloads.isEmpty()) {
// .....
}
}