美文网首页
Android中条目曝光统计

Android中条目曝光统计

作者: 馒头炖土豆 | 来源:发表于2023-02-08 14:34 被阅读0次
最近项目中需要对RecyclerView列表进行曝光统计,在经过数个方案的对比之后,我选择了下面这个方案,这是迄今为止我认为最好的方案借鉴链接如下:
https://juejin.cn/post/7028829716492582948

下面是整理的该方案的代码,具体实现逻辑讲解自行查看原文

import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout

//ExposureLayout
//用于曝光埋点统计,放在布局的最外层
//参考链接:https://juejin.cn/post/7028829716492582948
class ExposureLayout : FrameLayout {
    private val mExposureHandler by lazy { ExposureHandler(this) }

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    //添加到视图
    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        mExposureHandler.onAttachedToWindow()
    }

    //从视图中移除
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        mExposureHandler.onDetachedFromWindow()
    }

    //视图焦点改变
    override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
        super.onWindowFocusChanged(hasWindowFocus)
        mExposureHandler.onWindowFocusChanged(hasWindowFocus)
    }

    //视图可见性
    override fun onVisibilityAggregated(isVisible: Boolean) {
        super.onVisibilityAggregated(isVisible)
        mExposureHandler.onVisibilityAggregated(isVisible)
    }

    //曝光回调
    fun setExposureCallback(callback: IExposureCallback) {
        mExposureHandler.setExposureCallback( callback)
    }

    // 设置曝光条件 曝光区域大小,例如展示超过50%才算曝光
    fun setShowRatio(ratio: Float) {
        mExposureHandler.setShowArea(ratio)
    }

    // 设置曝光最小时间,例如必须曝光超过两秒才算曝光
    fun setTimeLimit(timeLimit:Int) {
        mExposureHandler.setTimeLimit(timeLimit)
    }
}
import android.graphics.Rect
import android.view.View
import android.view.ViewTreeObserver

//用于曝光埋点统计
//参考链接:https://juejin.cn/post/7028829716492582948
class ExposureHandler (private val view: View) : ViewTreeObserver.OnPreDrawListener {
    private var mAttachedToWindow = false //添加到视图中的状态
    private var mHasWindowFocus = true // 视图获取到焦点的状态,默认为true,避免某些场景不被调用
    private var mVisibilityAggregated = true //可见性的状态,默认为true,避免某些场景不被调用
    private var mExposure = false //当前是否处于曝光状态
    private var mExposureCallback: IExposureCallback? = null //曝光回调
    private var mStartExposureTime: Long = 0L//开始曝光时间戳
    private var mShowRatio: Float = 0f//曝光条件超过多大面积0~1f
    private var mTimeLimit: Int = 0 //曝光条件超过多久才算曝光,例如2秒(2000)
    private val mRect = Rect() //实时曝光面积

    //添加到视图时添加OnPreDrawListener
    fun onAttachedToWindow() {
        mAttachedToWindow = true
        view.viewTreeObserver.addOnPreDrawListener(this)
    }

    /**
     * 从视图中移除时去掉OnPreDrawListener
     * 尝试取消曝光
     */
    fun onDetachedFromWindow() {
        mAttachedToWindow = false
        view.viewTreeObserver.removeOnPreDrawListener(this)
        tryStopExposure()
    }

    /**
     * 视图焦点改变
     * 尝试取消曝光
     */
    fun onWindowFocusChanged(hasWindowFocus: Boolean) {
        mHasWindowFocus = hasWindowFocus
        if(!hasWindowFocus){
            tryStopExposure()
        }
    }

    /**
     * 可见性改变
     * 尝试取消曝光
     */
    fun onVisibilityAggregated(isVisible: Boolean) {
        mVisibilityAggregated = isVisible
        if(!isVisible){
            tryStopExposure()
        }
    }

    /**
     * 视图预绘制
     * 当曝光面积达到条件是尝试曝光
     * 当视图面积不满足条件时尝试取消曝光
     */
    override fun onPreDraw(): Boolean{
        val visible = view.getLocalVisibleRect(mRect)&&view.isShown //获取曝光面积和View的Visible
        if(!visible){
            tryStopExposure()//不达到曝光条件时尝试取消曝光
            return true
        }
        if(mShowRatio>0){//存在曝光面积限制条件时
            if(kotlin.math.abs(mRect.bottom-mRect.top)>view.height*mShowRatio &&kotlin.math.abs(mRect.right-mRect.left)>view.width*mShowRatio)
            {
                tryExposure()//达到曝光条件时尝试曝光
            }else{
                tryStopExposure()//不达到曝光条件时尝试取消曝光
            }
         }else{
            tryExposure()//达到曝光条件时尝试曝光
         }
        return true
    }
    /**
     * 曝光回调
     */
    fun setExposureCallback(callback: IExposureCallback) {
        mExposureCallback = callback
    }

    /**
     * 设置曝光面积条件
     */
    fun setShowArea(area: Float) {
        mShowRatio = area
    }

    /**
     * 设置曝光时间限制条件
     */
    fun setTimeLimit(index: Int) {
        this.mTimeLimit = index
    }

    /**
     * 尝试曝光
     */
    private fun tryExposure(){
        if(mAttachedToWindow && mHasWindowFocus && mVisibilityAggregated && !mExposure){
            mExposure = true//曝光中
            mStartExposureTime = System.currentTimeMillis()//曝光开始时间
            if(mTimeLimit == 0)
            {
                mExposureCallback?.show()//回调开始曝光
            }
        }
    }

    /**
     *尝试取消曝光
    */
    private fun tryStopExposure() {
        if(mExposure){
            mExposure = false//重置曝光状态
            if(mTimeLimit >0 && System.currentTimeMillis()- mStartExposureTime > mTimeLimit){
                //满足时长限制曝光
                mExposureCallback?.show()
            }
        }
    }
}
//用于曝光埋点统计
//参考链接:https://juejin.cn/post/7028829716492582948
interface IExposureCallback {
    fun show() //曝光
}

相关文章

网友评论

      本文标题:Android中条目曝光统计

      本文链接:https://www.haomeiwen.com/subject/mhuqkdtx.html