Android View onVisibilityChanged onAttachedToWindow onDetachedFromWindow
源码SDK: 24
简介
最近碰到一个问题,某一个Fragment里,有一个View上有一个动画,而当Fragment不可见的时候(切换到别的Fragment)。这时候,View的动画应该停止,以便节省主线程计算动画值,所以研究了View的onVisibilityChanged方法,在View不可见的时候关闭动画。
onAttachedToWindow与onDetachedFromWindow
研究onVisibilityChanged方法前,需要了解onAttachedToWindow,该方法是View添加到了Window窗口之后的一个回调。对应的是从Window窗口移除的时候回调onDetachedFromWindow。
何时触发attached与detached
上图中View需要添加到一个已经存在的试图树中,左边的试图树是一个已经完成过首次绘制的试图树。右边的是一个未完成首次绘制的试图树(比如Activity在onCreate里通过setContentView设置了布局,但是Activity只有onResume之后才会执行首次绘制)。
- 当执行addView方法之后,左边的View会回调onAttachedToWindow,而右边的不会回调onAttachedToWindow。
- 右边的View,在试图树执行首次绘制的时候,会回调其 onAttachedToWindow方法。
一个View是否attach到window上,framework是通过判断View中的mAttachInfo成员属性来判断的,也就是说整个试图树执行首次绘制的时候,ViewRootImpl会将自己的内部的mAttachInfo分发到所有的视图节点中。具体是通过调用View中的dispatchAttachedToWindow方法来下发。
android.view.View.java
/*** @param info the {@link android.view.View.AttachInfo} to associated with* this view*/
void dispatchAttachedToWindow(AttachInfo info, int visibility) {mAttachInfo = info;// .....略
}
View一旦收到AttachInfo对象后,也就代表View被attach到了window。
什么是首次绘制?
ViewRootImpl创建了,第一次执行performTravels方法。
android.view.ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {mContext = context;// ...mFirst = true; // true for the first time the view is added// ...
}private void performTraversals() {final View host = mView; // 根节点(一般是decorView)// ...if (mFirst) {// ...host.dispatchAttachedToWindow(mAttachInfo, 0);// ... }// ...mFirst = false;
}
既然View是通过mAttachInfo属性来判断是否已经添加到window,那么如果一个View被添加到一个ViewGroup,而这个ViewGroup未被添加到window,当然这个View也不会收到attachToWindow的回调。当这个ViewGroup被添加到window之后,View才有可能收到attachToWindow回调。
android.view.ViewGroup.java
private void addViewInner(View child, int index, LayoutParams params,boolean preventRequestLayout) {AttachInfo ai = mAttachInfo;if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));}
}
android.view.View.java
/*** @param info the {@link android.view.View.AttachInfo} to associated with* this view*/
void dispatchAttachedToWindow(AttachInfo info, int visibility) {mAttachInfo = info;if (mOverlay != null) {mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);}onAttachedToWindow(); // 在这里- -
}
onAttachedToWindow调用时机
- View添加到一个已经绘制过的试图树上,立马触发回调。
- View添加到一个没被绘制过的试图树上,等试图树首次绘制的时候,收到回调。
- View添加到一个ViewGroup上,但是该ViewGroup所在的试图树没有被添加到window上,不会回调。等待ViewGroup被执行情况1和情况2的时候,View也会收到回调。
onDetachedFromWindow调用时机
- View从一个ViewGroup上移除的时候,View曾经执行过onAttachedToWindow方法的话,会回调onDetachedFromWindow方法。
- View的祖宗节点(父亲或者父亲的父亲的父亲…节点)从试图树移除的时候,如果View曾经执行过onAttachedToWindow方法,会回调。
- 当window从WindowManager上移除的时候(比如activity执行onDestroy方法之后)。该window下的所有的试图节点都会收到onDetachedFromWindow回调。
onVisibilityChanged
onVisibilityChanged是否调用,依赖于View是否执行过onAttachedToWindow方法。也就是View是否被添加到Window上。
onVisibilityChanged被调用时机一
正常来说,调用View的setVisibility(@Visibility int visibility)方法,只要传入的visibility和当前View的visibility属性不一致就会触发onVisibilityChanged方法的调用。但是如果onAttachedToWindow方法未调用的情况下(View的mAttachInfo为空),是不会调用onVisibilityChanged的。
android.view.View.java
public void setVisibility(@Visibility int visibility) {setFlags(visibility, VISIBILITY_MASK);
}
void setFlags(int flags, int mask) {if ((changed & VISIBILITY_MASK) != 0) {// 只有曾经执行过onAttachedToWindow方法才能执行dispatchVisibilityChanged方法if (mAttachInfo != null) {dispatchVisibilityChanged(this, newVisibility);}}
}protected void dispatchVisibilityChanged(@NonNull View changedView,@Visibility int visibility) {// 对View来说,执行dispatchVisibilityChanged方法就是调用onVisibilityChanged方法onVisibilityChanged(changedView, visibility);
}
onVisibilityChanged被调用时机二
当View的祖先节点(父亲的父亲的…父亲),的visibility改变之后,ViewGroup会通过dispatchVisibilityChanged传递给所有的子View该事件(因为如果祖先不可见,子孙自然不可见…)。
比如,当Activity执行onStop生命周期的时候,所有的View节点都会受到onVisibilityChanged生命周期,此时的visibility为INVISIBLE,changedView为Activity的根节点,也就是DecorView。调用栈如下:
at test.com.myapplication.MainActivity$1.onVisibilityChanged(MainActivity.java:439)
at android.view.View.dispatchVisibilityChanged(View.java:10308)
at android.view.ViewGroup.dispatchVisibilityChanged(ViewGroup.java:1280)
at android.view.ViewGroup.dispatchVisibilityChanged(ViewGroup.java:1280)
at android.view.ViewGroup.dispatchVisibilityChanged(ViewGroup.java:1280)
at android.view.ViewGroup.dispatchVisibilityChanged(ViewGroup.java:1280)
at android.view.ViewGroup.dispatchVisibilityChanged(ViewGroup.java:1280)
at android.view.View.setFlags(View.java:11533)
at android.view.View.setVisibility(View.java:8069)
at android.app.ActivityThread.updateVisibility(ActivityThread.java:3902)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:3922)
at android.app.ActivityThread.-wrap25(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1512)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6121)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
android.view.ViewGroup.java
protected void dispatchVisibilityChanged(View changedView, int visibility) {super.dispatchVisibilityChanged(changedView, visibility);final int count = mChildrenCount;final View[] children = mChildren;for (int i = 0; i < count; i++) {children[i].dispatchVisibilityChanged(changedView, visibility);}
}
但是,祖先如果从invisible设置成visible,子View虽然会收到onVisibilityChanged回调,但是子View可能自身就是invisible,所以仍然不可见,并不是祖先可见子孙就可见
也就是说View即使是inVisible或者gone,也能收到onVisibilityChanged回调。
onVisibilityChanged被调用时机三
当View或者View的祖先节点被添加到window上时,View的dispatchAttachedToWindow方法将会被调用,在View的dispatchAttachedToWindow方法里,将会调用View的onVisibilityChanged方法。
总结
- onAttachedToWindow被调用,即代表着View被添加到了一个绘制过的视图树中。
- onAttachedToWindow和onDetachedFromWindow可以被调用多次。
- 当View被添加到已经绘制过的视图树上时,onAttachedToWindow会被立即执行,接着onVisibilityChanged也会立即执行。
- 当View从视图上移除时,如果onAttachedToWindow方法曾经执行过,那么onDetachedFromWindow将会被执行。
- onVisibilityChanged被调用的前提是View执行过onAttachedToWindow方法。
- 判断View是否执行过onAttachedToWindow的依据是View里的mAttachInfo对象不为空。
Android View onVisibilityChanged onAttachedToWindow onDetachedFromWindow相关推荐
- View的onAttachedToWindow, onDetachedFromWindow的调用时机,使用场景是什么?
原始网页直通车 调用时机 Attached 附加的意思,当 View 附加到 Window 的时候,就会回调 onAttachedToWindow . Detached 分离,拆卸的意思,与 Atta ...
- Android View生命周期
View的关键生命周期为: > 构造View() --> onFinishInflate() --> onAttachedToWindow() --> onMeasure() ...
- android View类详解。
Android中的View类代表用户界面中基本的构建块.一个View在屏幕中占据一个矩形区域.并且负责绘制和事件处理.View是所有widgets的基础类,widgets是我们通常用于创建和用户交互的 ...
- 由View的onAttachedToWindow引发的图片轮播问题探究
由View的onAttachedToWindow引发的图片轮播问题探究 文章目录 由View的onAttachedToWindow引发的图片轮播问题探究 前言 Handle#removeCallbac ...
- Android运行时候报错:android.view.InflateException: Binary XML file line #19: Binary XML file lin
Android运行时候报错:android.view.InflateException: Binary XML file line #19: Binary XML file lin 这个问题自己大致在 ...
- android.view.InflateException: Binary XML file line #7: Binary XML file line #7
错误如下 11-21 08:19:44.040 3608-3608/com.leon.oldrecyclerview E/AndroidRuntime: FATAL EXCEPTION: main ...
- Android编程:解决异常“android.view.InflateException: Binary XML file line # : Error inflating class”
我一般都是在1和4 遇到,记录一下. 今天写程序发现一个问题,就是XML中报出Android.view.InflateException异常,可能的原因有: 1.XML中使用到得组件名称是否书写正确( ...
- Android View框架的measure机制
Android中View框架的工作机制中,主要有三个过程: 1.View树的测量(measure)Android View框架的measure机制 http://www.cnblogs.com/xyh ...
- android view设置按钮颜色_Android 主题换肤技术方案分析
写在前面 Android TV 电视开发,主题换肤,我感觉有两种层级的方式,一种是 系统级,另一种 是应用级, 我记得很早在 Linux 桌面开发的时候,我们之前的公司在GTK+上也实现了一套换肤UI ...
- android 虚方法,尝试在空对象引用上调用虚方法’android.view.View android.view.View.getRootView()’...
我收到这个错误, "Attempt to invoke virtual method 'android.view.View android.view.View.getRootView()' ...
最新文章
- @Controller和@RestController的区别?
- java selenium (九) 常见web UI 元素操作 及API使用
- java--内存管理的几点小技巧
- S3C2410时钟部分总结
- new housing price at shanghai
- 使用一阶微分对图像锐化
- Vue学习(组件传参)-学习笔记
- 解决文件路径的问题的总结
- 单例模式饿汉式/懒汉式的区别
- iOS - Phone 电话
- 工作小总结(字符串包含,获取当前页面的url等系列问题)
- 蔡学镛:写SOP(标准作业程序)就是写程序
- windows 10和windows server 2016系统AD的administrator密码修改
- 全球十大咨询公司比较
- python 转换数字为中文的大写
- Java实现随机生成车牌号
- java opencv 去噪,opencv教程-图像去噪与修复
- GetContactInfoUtils(一个获取手机联系人名称,电话,头像的工具类)
- 基于matlab振动信号处理,基于PC及MATLAB的振动信号处理的实现
- 三年级下册分数计算机题,三年级下册数学卷子题
热门文章
- html缓存失败是什么,HTML5离线“应用程序缓存错误事件:清单读取失败(-1)”...
- Tool-X:在AndroidUbuntu平台安装Kali的各种小工具
- Hbase API实现倒序查询
- NIO中的ByteBuffer读取中文错误的解决方法:MalformedInputException
- 女孩子学前教育计算机,女孩子考什么专业比较好 适合女生的五大专业
- Redis 和 memcached 区别(二)
- Linux下PHP开发环境搭建(Apache2.4+PHP7.1+MySQL8.0)
- 根据缺口的模式选股买股票,python 学习代码
- javascript事件委托和jQuery事件绑定on、off 和one以及on绑定多个事件(重要)
- ssl 1606 选课