源码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之后才会执行首次绘制)。

  1. 当执行addView方法之后,左边的View会回调onAttachedToWindow,而右边的不会回调onAttachedToWindow。
  2. 右边的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调用时机

  1. View添加到一个已经绘制过的试图树上,立马触发回调。
  2. View添加到一个没被绘制过的试图树上,等试图树首次绘制的时候,收到回调。
  3. View添加到一个ViewGroup上,但是该ViewGroup所在的试图树没有被添加到window上,不会回调。等待ViewGroup被执行情况1和情况2的时候,View也会收到回调。

onDetachedFromWindow调用时机

  1. View从一个ViewGroup上移除的时候,View曾经执行过onAttachedToWindow方法的话,会回调onDetachedFromWindow方法。
  2. View的祖宗节点(父亲或者父亲的父亲的父亲…节点)从试图树移除的时候,如果View曾经执行过onAttachedToWindow方法,会回调。
  3. 当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方法。

总结

  1. onAttachedToWindow被调用,即代表着View被添加到了一个绘制过的视图树中。
  2. onAttachedToWindow和onDetachedFromWindow可以被调用多次。
  3. 当View被添加到已经绘制过的视图树上时,onAttachedToWindow会被立即执行,接着onVisibilityChanged也会立即执行。
  4. 当View从视图上移除时,如果onAttachedToWindow方法曾经执行过,那么onDetachedFromWindow将会被执行。
  5. onVisibilityChanged被调用的前提是View执行过onAttachedToWindow方法。
  6. 判断View是否执行过onAttachedToWindow的依据是View里的mAttachInfo对象不为空。

Android View onVisibilityChanged onAttachedToWindow onDetachedFromWindow相关推荐

  1. View的onAttachedToWindow, onDetachedFromWindow的调用时机,使用场景是什么?

    原始网页直通车 调用时机 Attached 附加的意思,当 View 附加到 Window 的时候,就会回调 onAttachedToWindow . Detached 分离,拆卸的意思,与 Atta ...

  2. Android View生命周期

    View的关键生命周期为: > 构造View() --> onFinishInflate() --> onAttachedToWindow() --> onMeasure() ...

  3. android View类详解。

    Android中的View类代表用户界面中基本的构建块.一个View在屏幕中占据一个矩形区域.并且负责绘制和事件处理.View是所有widgets的基础类,widgets是我们通常用于创建和用户交互的 ...

  4. 由View的onAttachedToWindow引发的图片轮播问题探究

    由View的onAttachedToWindow引发的图片轮播问题探究 文章目录 由View的onAttachedToWindow引发的图片轮播问题探究 前言 Handle#removeCallbac ...

  5. 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 这个问题自己大致在 ...

  6. 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   ...

  7. Android编程:解决异常“android.view.InflateException: Binary XML file line # : Error inflating class”

    我一般都是在1和4 遇到,记录一下. 今天写程序发现一个问题,就是XML中报出Android.view.InflateException异常,可能的原因有: 1.XML中使用到得组件名称是否书写正确( ...

  8. Android View框架的measure机制

    Android中View框架的工作机制中,主要有三个过程: 1.View树的测量(measure)Android View框架的measure机制 http://www.cnblogs.com/xyh ...

  9. android view设置按钮颜色_Android 主题换肤技术方案分析

    写在前面 Android TV 电视开发,主题换肤,我感觉有两种层级的方式,一种是 系统级,另一种 是应用级, 我记得很早在 Linux 桌面开发的时候,我们之前的公司在GTK+上也实现了一套换肤UI ...

  10. android 虚方法,尝试在空对象引用上调用虚方法’android.view.View android.view.View.getRootView()’...

    我收到这个错误, "Attempt to invoke virtual method 'android.view.View android.view.View.getRootView()' ...

最新文章

  1. @Controller和@RestController的区别?
  2. java selenium (九) 常见web UI 元素操作 及API使用
  3. java--内存管理的几点小技巧
  4. S3C2410时钟部分总结
  5. new housing price at shanghai
  6. 使用一阶微分对图像锐化
  7. Vue学习(组件传参)-学习笔记
  8. 解决文件路径的问题的总结
  9. 单例模式饿汉式/懒汉式的区别
  10. iOS - Phone 电话
  11. 工作小总结(字符串包含,获取当前页面的url等系列问题)
  12. 蔡学镛:写SOP(标准作业程序)就是写程序
  13. windows 10和windows server 2016系统AD的administrator密码修改
  14. 全球十大咨询公司比较
  15. python 转换数字为中文的大写
  16. Java实现随机生成车牌号
  17. java opencv 去噪,opencv教程-图像去噪与修复
  18. GetContactInfoUtils(一个获取手机联系人名称,电话,头像的工具类)
  19. 基于matlab振动信号处理,基于PC及MATLAB的振动信号处理的实现
  20. 三年级下册分数计算机题,三年级下册数学卷子题

热门文章

  1. html缓存失败是什么,HTML5离线“应用程序缓存错误事件:清单读取失败(-1)”...
  2. Tool-X:在AndroidUbuntu平台安装Kali的各种小工具
  3. Hbase API实现倒序查询
  4. NIO中的ByteBuffer读取中文错误的解决方法:MalformedInputException
  5. 女孩子学前教育计算机,女孩子考什么专业比较好 适合女生的五大专业
  6. Redis 和 memcached 区别(二)
  7. Linux下PHP开发环境搭建(Apache2.4+PHP7.1+MySQL8.0)
  8. 根据缺口的模式选股买股票,python 学习代码
  9. javascript事件委托和jQuery事件绑定on、off 和one以及on绑定多个事件(重要)
  10. ssl 1606 选课