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

完整源码

待补充

相关推荐
晓蓝WQuiet2 分钟前
《鸟哥的Linux私房菜》笔记 第七至十六章
linux·运维·笔记
ljt27249606614 分钟前
Vue笔记(一)--模板
前端·vue.js·笔记
山岚的运维笔记5 分钟前
Bash 专业人员笔记 -- 第 11 章:`true`、`false` 和 `:` 命令
linux·运维·服务器·开发语言·笔记·学习·bash
Honker_yhw12 分钟前
大数据管理与应用系列丛书《数据挖掘》(吕欣等著)读书笔记-偏相关分析
笔记·学习
许长安13 分钟前
C++ 原子变量与内存序:从std::atomic到release/acquire
开发语言·数据结构·c++·经验分享·笔记
OBiO201316 分钟前
靶向骨的腺相关病毒(AAV)血清型及启动子选择
笔记
白云偷星子1 小时前
云原生笔记8
笔记·云原生
alexhilton9 小时前
Android上的ZeroMQ:用发布/订阅模式连接Linux服务
android·kotlin·android jetpack
风别鹤9 小时前
Cocos Creator无法识别Android SDK
android
应用市场9 小时前
Android A/B 无缝更新机制深度剖析
android·网络