最近项目中需要对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() //曝光
}
网友评论