android view 曝光,View的有效曝光监控(下)|ScrollView NestScrollView篇

本文介绍了如何在ScrollView和NestScrollView中实现滑动监控以及View的曝光监测。通过分析ViewTreeObserver的接口,利用onScrollChanged和onGlobalLayout监听滑动变化,结合自定义适配器ExposureViewAdapter,实现对View曝光状态的跟踪,确保超过1.5秒的曝光时长。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面试官:老哥那么我们继续探讨下这个问题啊。Scrollview和NestScrollView怎么监控呢。

我:???黑人老哥又特么来了。

16adf2143793d70ef23aba0683988cb1.png

分析问题

还是和上篇文章一样,我们先看下要解决哪些问题。

ScrollView NestScrollView 的滑动监控怎么做。

View有没有像RecyclerView一样的attach和detch方法,超过1.5s的曝光时间。

View出现一半。

滑动监控

一般人肯定告诉你,这个你自定义个scrollview,然后在onScrollChanged实现个滑动监听的回调什么的。不好意思,我偏不,带你看看另外一个神奇的方法。

先给大家介绍下ViewTreeObserver里面所包含的一些接口。

内部类接口

备注

ViewTreeObserver.OnPreDrawListener

当视图树将要被绘制时,会调用的接口

ViewTreeObserver.OnGlobalLayoutListener

当视图树的布局发生改变或者View在视图树的可见状态发生改变时会调用的接口

ViewTreeObserver.OnGlobalFocusChangeListener

当一个视图树的焦点状态改变时,会调用的接口

ViewTreeObserver.OnScrollChangedListener

当视图树的一些组件发生滚动时会调用的接口

ViewTreeObserver.OnTouchModeChangeListener

当视图树的触摸模式发生改变时,会调用的接口格

各位老哥有没有发现一些奇怪的东西混在里面,哈哈哈。

惯例分析下源码

理论上来说,所有视图状态之类的都是和ViewRootImp相关的。特别是ViewTreeObserver相关的,所以我们的源码分析也是从ViewRootImp开始的。

class ViewRootImp {

// 根视图绘制

private boolean draw(boolean fullRedrawNeeded) {

Surface surface = mSurface;

if (!surface.isValid()) {

return false;

}

if (DEBUG_FPS) {

trackFPS();

}

if (!sFirstDrawComplete) {

synchronized (sFirstDrawHandlers) {

sFirstDrawComplete = true;

final int count = sFirstDrawHandlers.size();

for (int i = 0; i< count; i++) {

mHandler.post(sFirstDrawHandlers.get(i));

}

}

}

scrollToRectOrFocus(null, false);

if (mAttachInfo.mViewScrollChanged) {

mAttachInfo.mViewScrollChanged = false;

// 调用viewtree的滑动监听

mAttachInfo.mTreeObserver.dispatchOnScrollChanged();

}

.....

return useAsyncReport;

}

}

复制代码

上面的代码可以看出,当mAttachInfo.mViewScrollChanged这个状态位被设置成true的情况下,就会通知viewTree调用滑动监听了。

那么我们的切入点就很简单了,什么时候谁把这个值设置成ture了,是不是就会触发滑动监听了呢。

class View {

final static class AttachInfo {

/**

* Set to true if a view has been scrolled.

*/

@UnsupportedAppUsage

boolean mViewScrollChanged;

}

/**

* This is called in response to an internal scroll in this view (i.e., the

* view scrolled its own contents). This is typically as a result of

* {@link #scrollBy(int, int)} or {@link #scrollTo(int, int)} having been

* called.

*

* @param l Current horizontal scroll origin.

* @param t Current vertical scroll origin.

* @param oldl Previous horizontal scroll origin.

* @param oldt Previous vertical scroll origin.

*/

protected void onScrollChanged(int l, int t, int oldl, int oldt) {

notifySubtreeAccessibilityStateChangedIfNeeded();

if (AccessibilityManager.getInstance(mContext).isEnabled()) {

postSendViewScrolledAccessibilityEventCallback(l - oldl, t - oldt);

}

mBackgroundSizeChanged = true;

mDefaultFocusHighlightSizeChanged = true;

if (mForegroundInfo != null) {

mForegroundInfo.mBoundsChanged = true;

}

final AttachInfo ai = mAttachInfo;

if (ai != null) {

ai.mViewScrollChanged = true;

}

if (mListenerInfo != null && mListenerInfo.mOnScrollChangeListener != null) {

mListenerInfo.mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);

}

}

}

复制代码

View.AttachInfo是View的内部类,其注释已经描述了,当view滑动的时候把这个值设置成true。onScrollChanged也是View的protected的方法,而当ScrollView和NestScrollView的滑动状态被改变的时候就会调用这个方法,而这个方法内则就会把状态设置成true。

测试结果

经过在下的测试吧,OnScrollChangedListener在ScrollView和NestScrollView滑动的时候都会触发回调哦。而上述代码分析,则可以说明当两个滑动组件滑动的时候就会触发对应的回调监听。

View 出现一半

这个监控方法还是和上篇文章一样,请各位大佬直接看上篇文章就好了。

1.5s的曝光时长

先回到之前的文章提到onAttachedToWindow onDetachedFromWindow的两个方法,这两个可以用吗?答案肯定是不行的。那么我们应该怎么办呢??

没有枪没有炮,还是自己造吧。

interface ExposeViewAdapter {

fun setExposeListener(listener: (Float) -> Unit)

fun setExposeListener(listener: OnExposeListener)

fun onVisibleChange(isCover: Boolean)

}

复制代码

首先我们可以先提供一个适配器,提供onVisibleChange这个方法来代替onAttachedToWindow onDetachedFromWindow。

class ExposeScrollChangeListener(scrollView: ViewGroup) :

ViewTreeObserver.OnScrollChangedListener, ViewTreeObserver.OnGlobalLayoutListener {

private val rootView: ViewGroup? = scrollView.getChildAt(0) as ViewGroup?

private val views = hashSetOf()

private var lastChildCount = 0

init {

}

override fun onScrollChanged() {

views.forEach {

val exposeView = it as ExposeViewAdapter

exposeView.onVisibleChange(it.visibleRect())

}

}

private fun checkViewSize() {

rootView?.apply {

lastChildCount = childCount

getChildExpose(rootView)

}

}

private fun getChildExpose(view: View?) {

view?.let {

if (it is ExposeViewAdapter) {

views.add(it)

}

if (view is ViewGroup) {

//遍历ViewGroup,是子view加1,是ViewGroup递归调用

for (i in 0 until view.childCount) {

val child = view.getChildAt(i)

if (child is ExposeViewAdapter) {

views.add(child)

}

if (child is ViewGroup) {

getChildExpose(child)

}

}

}

}

}

override fun onGlobalLayout() {

val timeUsage = System.currentTimeMillis()

checkViewSize()

Log.i("expose", "timeCoast:${System.currentTimeMillis() - timeUsage}")

}

}

复制代码

首先我们需要监控onGlobalLayout这个方法,在这个方法触发的情况下,去扫描当前的ViewTree,去获取实现了ExposeViewAdapter的所有的View。当滑动监听触发的时候调用之前的view是否被遮挡的方法来判断当前的view是不是在视图上出现了,然后调用onVisibleChange来通知视图是否已经从window上移除。

最后

面试官:哎哟不错哟。

51301053877e4e02f7b61dcbd0e1276c.png

我:谦虚有理的小菜逼。

面试官:这种方式感觉还是不够智能,如果让你用动态插桩呢。

我:打扰了,二营长,把我的意大利炮抬过来。

面试官:回家继续等通知把。

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[View的有效曝光监控(下)|ScrollView NestScrollView篇]http://www.zyiz.net/tech/detail-140150.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值