Android笔记(三十七):封装一个RecyclerView Item曝光工具——用于埋点上报

背景

项目中首页列表页需要统计每个item的曝光情况,给产品运营提供数据报表分析用户行为,于是封装了一个通用的列表Item曝光工具,方便曝光埋点上报

源码分析

  • 核心就是监听RecyclerView的滚动,在滚动状态为SCROLL_STATE_IDLE的时候开始计算哪些item是可见的
kotlin 复制代码
private fun calculateVisibleItemInternal() {
        if (!isRecording) {
            return
        }
        val lastRange = currVisibleRange
        val currRange = findItemVisibleRange()
        val currStartTime = System.currentTimeMillis()


        val newVisibleItemPosList = createCurVisiblePosList(lastRange, currRange)

        visibleItemCheckTasks.forEach {
            it.updateVisibleRange(currRange)
        }
        if (newVisibleItemPosList.isNotEmpty()) {
            VisibleCheckTimerTask(newVisibleItemPosList, this, threshold).also {
                visibleItemCheckTasks.add(it)
            }.execute()
        }
        val it = currVisibleItemPosList.iterator()
        while (it.hasNext()) {
            val entry = it.next()
            if (entry !in currRange) {
                it.remove()
            }
        }
        currVisibleItemPosList.addAll(newVisibleItemPosList)
        currRangeVisibleStartTime = currStartTime
        currVisibleRange = currRange
    }
  • 根据LayoutManager找出当前可见item的范围,剔除掉显示不全的item
kotlin 复制代码
private fun findItemVisibleRange(): IntRange {
        return when (val lm = currRecyclerView?.layoutManager) {
            is GridLayoutManager -> {
                val first = lm.findFirstVisibleItemPosition()
                val last = lm.findLastVisibleItemPosition()
                return fixCurRealVisibleRange(first, last)
            }
            is LinearLayoutManager -> {
                val first = lm.findFirstVisibleItemPosition()
                val last = lm.findLastVisibleItemPosition()
                return fixCurRealVisibleRange(first, last)
            }
            is StaggeredGridLayoutManager -> {
                val firstItems = IntArray(lm.spanCount)
                lm.findFirstVisibleItemPositions(firstItems)
                val lastItems = IntArray(lm.spanCount)
                lm.findLastVisibleItemPositions(lastItems)

                val first = when (RecyclerView.NO_POSITION) {
                    firstItems[0] -> {
                        firstItems[lm.spanCount - 1]
                    }
                    firstItems[lm.spanCount - 1] -> {
                        firstItems[0]
                    }
                    else -> {
                        min(firstItems[0], firstItems[lm.spanCount - 1])
                    }
                }

                val last = when (RecyclerView.NO_POSITION) {
                    lastItems[0] -> {
                        lastItems[lm.spanCount - 1]
                    }
                    lastItems[lm.spanCount - 1] -> {
                        lastItems[0]
                    }
                    else -> {
                        max(lastItems[0], lastItems[lm.spanCount - 1])
                    }
                }
                return fixCurRealVisibleRange(first, last)
            }
            else -> {
                IntRange.EMPTY
            }
        }
    }
  • 对可见的item进行分组,形成一个位置列表,上一次和这一次的分开组队
kotlin 复制代码
private fun createCurVisiblePosList(lastRange: IntRange, currRange: IntRange): List<Int> {
        val result = mutableListOf<Int>()
        currRange.forEach { pos ->
            if (pos !in lastRange) {
                result.add(pos)
            }
        }
        return result
    }
  • 将分组好的列表装进一个定时的task,在延迟一个阈值的时间后执行onVisibleCheck
kotlin 复制代码
class VisibleCheckTimerTask(
    input: List<Int>,
    private val callback: VisibleCheckCallback,
    private val delay: Long
) : Runnable {

    private val visibleList = mutableListOf<Int>()
    private var isExecuted = false

    init {
        visibleList.addAll(input)
    }

    fun updateVisibleRange(keyRange: IntRange) {
        val iterator = visibleList.iterator()
        while (iterator.hasNext()) {
            val entry = iterator.next()
            if (entry !in keyRange) {
                iterator.remove()
            }
        }
    }

    override fun run() {
        callback.onVisibleCheck( this, visibleList)
    }

    fun execute() {
        if (isExecuted) {
            return
        }
        mHandler.postDelayed(this, delay)
        isExecuted = true
    }

    fun cancel() {
        mHandler.removeCallbacks(this)
    }
}
  • 达到阈值后,再获取一遍可见item的范围,对于仍然可见的item回调onItemShow
kotlin 复制代码
override fun onVisibleCheck(task: VisibleCheckTimerTask, visibleList: List<Int>) {
        val visibleRange = findItemVisibleRange()
        visibleList.forEach {
            if (it in visibleRange) {
                notifyItemShow(it)
            }
        }
        visibleItemCheckTasks.remove(task)
    }

完整源码

待补充

相关推荐
小箌14 分钟前
html css 笔记
css·笔记·html
pengyu43 分钟前
系统化掌握Flutter开发之主题(Theme)(一):筑基之旅
android·flutter·dart
hillstream31 小时前
jira操作笔记
笔记·jira
&有梦想的咸鱼&3 小时前
Android Retrofit 框架数据转换模块深入源码分析(四)
android·retrofit
pengyu3 小时前
系统化掌握Flutter组件之InteractiveViewer:释放你的双手
android·flutter·dart
掘金一周4 小时前
老板花一万大洋让我写的艺术工作室官网?! HeroSection 再度来袭!(Three.js) | 3.13
android·前端·后端
mercyT4 小时前
Kotlin学习笔记之基础知识
笔记·学习·kotlin
为几何欢5 小时前
【学习笔记】《逆向工程核心原理》03.abex‘crackme-2、函数的调用约定、视频讲座-Tut.ReverseMe1
笔记·学习·逆向·crackme·逆向工程·vb逆向
Zhouqi_Hua5 小时前
LLM论文笔记 23: Meta Reasoning for Large Language Models
论文阅读·人工智能·笔记·深度学习·语言模型·自然语言处理
NEET_LH5 小时前
Python个人学习笔记(14):函数(匿名函数、内置函数(下)、三元表达式)
笔记·python·学习