需求
数据埋点,要采集列表里显示的所有卡片。展开讲就是进入界面时把所有已展示的item统计出来,滑动时把新展示的item再统计进来
代码实现
interface OnExposeListener {
fun onItemViewVisible(list: MutableList<Int>)
}
class ScrollViewExposeUtil {
lateinit var mRecyclerView: RecyclerView
lateinit var mListener: OnExposeListener
var lastStart: Int = 0
var lastEnd: Int = 0
fun setRecyclerExposeListener(recyclerView: RecyclerView?, listener: OnExposeListener) {
if (recyclerView == null || recyclerView.visibility != View.VISIBLE) {
return
}
mRecyclerView = recyclerView
mListener = listener
mRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_DRAGGING
|| newState == RecyclerView.SCROLL_STATE_SETTLING) {
handleVisibleItems()
}
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
handleVisibleItems()
}
})
}
fun handleVisibleItems() {
if (mRecyclerView == null || mRecyclerView.visibility != View.VISIBLE ||
!mRecyclerView.isShown || !mRecyclerView.getGlobalVisibleRect(Rect())
) {
return
}
try {
var range: IntArray? = IntArray(2)
when (val manager = mRecyclerView.layoutManager) {
is LinearLayoutManager -> {
range = findRangeLinear(manager)
}
is GridLayoutManager -> {
range = findRangeGrid(manager)
}
is StaggeredGridLayoutManager -> {
range = findRangeStaggeredGrid(manager)
}
}
if (range == null || range.size < 2) {
return
}
Log.e("linmutang", " range[0] = ${range[0]} range[1] = ${range[1]}")
Log.e("linmutang", " lastStart =$lastStart lastEnd = $lastEnd")
if (lastEnd == 0) {
var list = arrayListOf<Int>()
for (i in range[0]..range[1]) {
list.add(i)
}
mListener.onItemViewVisible(list)
lastEnd = range[1]
return
}
if (range[1] > lastEnd || range[0] > lastStart) {
var list = arrayListOf<Int>()
for (i in (lastEnd + 1)..range[1]) {
list.add(i)
}
mListener.onItemViewVisible(list)
lastStart = range[0]
lastEnd = range[1]
return
}
if (range[0] < lastStart || range[1] < lastEnd) {
var list = arrayListOf<Int>()
for (i in range[0] until lastStart) {
list.add(i)
}
mListener.onItemViewVisible(list)
lastStart = range[0]
lastEnd = range[1]
return
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun findRangeLinear(manager: LinearLayoutManager): IntArray? {
val range = IntArray(2)
range[0] = manager.findFirstVisibleItemPosition()
range[1] = manager.findLastVisibleItemPosition()
return range
}
private fun findRangeGrid(manager: GridLayoutManager): IntArray? {
val range = IntArray(2)
range[0] = manager.findFirstVisibleItemPosition()
range[1] = manager.findLastVisibleItemPosition()
return range
}
private fun findRangeStaggeredGrid(manager: StaggeredGridLayoutManager): IntArray? {
val startPos = IntArray(manager.spanCount)
val endPos = IntArray(manager.spanCount)
manager.findFirstVisibleItemPositions(startPos)
manager.findLastVisibleItemPositions(endPos)
return findRange(startPos, endPos)
}
private fun findRange(startPos: IntArray, endPos: IntArray): IntArray {
var start = startPos[0]
var end = endPos[0]
for (i in 1 until startPos.size) {
if (start > startPos[i]) {
start = startPos[i]
}
}
for (i in 1 until endPos.size) {
if (end < endPos[i]) {
end = endPos[i]
}
}
return intArrayOf(start, end)
}
}
实现思路比较简单,调用 ScrollView 的 OnScrollListener 方法。滑动时找到当前所有显示item的第一个与最后一个的位置,根据这个范围值对比上次的范围值,确定哪些是新出现的item,把这些记录在数组中返回出去,可以批量处理
使用
ScrollViewExposeUtil().setRecyclerExposeListener(binding.rvData, object : OnExposeListener {
override fun onItemViewVisible(list: MutableList<Int>) {
for (i in list) {
Log.e(TAG, mData[i] )
}
}
})
延伸:采集展现超过50%的item
private void setCallbackForLogicVisibleView(View view, int position, int orientation) {
if (view == null || view.getVisibility() != View.VISIBLE ||
!view.isShown() || !view.getGlobalVisibleRect(new Rect())) {
return;
}
Rect rect = new Rect();
boolean cover = view.getGlobalVisibleRect(rect);
boolean visibleHeightEnough = orientation == OrientationHelper.VERTICAL && rect.height() > view.getMeasuredHeight() / 2;
boolean visibleWidthEnough = orientation == OrientationHelper.HORIZONTAL && rect.width() > view.getMeasuredWidth() / 2;
boolean isItemViewVisibleInLogic = visibleHeightEnough || visibleWidthEnough;
if (cover && isItemViewVisibleInLogic) {
}
}
|