Android:RecyclerView数据采集、埋点

需求

数据埋点,要采集列表里显示的所有卡片。展开讲就是进入界面时把所有已展示的item统计出来,滑动时把新展示的item再统计进来

代码实现

/**
 * 创建人:linmutang
 * 创建时间:9/29/21
 */
interface OnExposeListener {

    /**
     *      返回刚出现的item数组
     * @param list MutableList<Int>
     */
    fun onItemViewVisible(list: MutableList<Int>)

}

/**
 *
 *      用于ScrollView采点用
 *
 * 创建人:linmutang
 * 创建时间:9/29/21
 */
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) {

                //关注:SCROLL_STATE_IDLE:停止滚动;  SCROLL_STATE_DRAGGING: 用户慢慢拖动
                // 关注:SCROLL_STATE_SETTLING:惯性滚动
                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() {

        //View.getGlobalVisibleRect(new Rect()),true表示view视觉可见,无论可见多少。
        if (mRecyclerView == null || mRecyclerView.visibility != View.VISIBLE ||
            !mRecyclerView.isShown || !mRecyclerView.getGlobalVisibleRect(Rect())
        ) {
            return
        }
        
        //保险起见,为了不让统计影响正常业务,这里做下try-catch
        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


    /**
     *      在上面过滤的基础上再过滤一遍
     *
     * @param view 可见item的view
     * @param position 可见item的position
     * @param orientation recyclerView的方向
     */
    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);

        //  item逻辑上可见:可见且可见高度(宽度)>view高度(宽度)50%才行
        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) {
           //   此处是符合要求的 position
        }

    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值