导致View树遍历的时机

遍历View树意味着整个View需要重新对其包含的子视图分配大小并重绘,导致重新遍历的原因主要有三个
1.视图本身内部状况变化引起重绘。
2.第二个是View树内部添加或者删除了View。
3.View本身的大小及可见性发生变化。
能引起View树重新遍历的操作,总的来讲可以分为三类。一类是导致视图大小发生变化;第二类是导致ViewGroup重新为子视图分配位置;第三类是视图显示情况发生变化需要重绘。这三类情况最后都直接或者间接调用到三个函数,分别为invalidate()、requesetLayout()及requestFocus(),而这三个函数最终都会调用到ViewRoot中的schuedeuleTravesals()函数,该函数然后发起一个异步消息,消息处理中调用performTraversals()开始对整个View进行重新遍历。
能导致调用invalidate()函数的包含三种情况:
1、当应用程序改变视图显示属性时,调用setVisibility()。
2、当改变视图Selected状态时,调用setSelected()。
3、当改变视图Enable状态时,调用setEnable()函数。
能导致调用requestLayout()函数的情况包含两种:
1、当应用程序改变视图显示属性时,调用setVisibility(),由于显示或者不显示将影响其他兄弟视图的位置,因 此会调用到requestLayout()。
2、应用程序直接或间接调用该函数,间接调用是指应用程序调用了View类的其他函数,从而间接调用到requestLayout()。
requestFocus()一般由程序直接调用。
refreshDrawableList()
该函数的作用是根据状态标识,为视图赋予不同的Drawable对象。
1、给mPrivateFlags添加DRAWABLE_STATE_DIRTY标识,该标识仅在后面调用getDrawableState()函数中用于判断是否发生状态变化。
2、调用drawableSateChanged()。该函数是一个protected类型,只有Framework中的View子类可以重载该函数,一般来讲,就是ViewGroup重载了该函数。ViewGroup中重载该函数的作用仅仅是为了配合FLAG_ADD_STATES_FROM_CHILDRN标识,后面将会讲到该标识的作用。View类内部,该函数的默认实现包括以下几项。
(1)调用getDrawableState()获得视图的当前状态,然后再调用onCreateDrawableState()将这些状态转换为一个int[]型数组,这个数组的内部格式是预先定好的,DrawableStateList类可以识别该int[]数组。最后再将第一步设置的标识进行清除。
(2)mBGDrawable变量是该视图的背景图,它包含一个setState()函数,函数的参数正是上一步获得的int[]型数组,该函数内部会根据该int[]型数组为mBGDrawable找到真正的Drawable对象。
3、如果该视图有父视图,则调用父视图的childDrawableStateChanged()。父视图要么是ViewGroup类,要么是一个ViewRoot类。
ensureTouchMode()
这个函数的命名不够准确,从该函数内部分析来看,其作用是在Touch和非Touch直接切换时对视图的焦点状态进行处理。
setVisibility()
该函数用于改变视图的可视状态,可视状态包括GONE、VISIBLE、INVSIBLE三种。该函数内部很简单,首先调用setFlags(),然后调用mBGDrawable.setVisible()函数改变视图背景图的显示状态。
setEnable()
Enable状态仅仅是内部的一个逻辑,不会引起重新布局,仅仅是引起视图的重绘。
1、给mPrivateFlags变量添加ENABLE或者DISABLE标识,这由setEnalbe()的参数决定。
2、调用refreshDrawableState()重新获取背景图。
3、调用invalidate()请求View树重绘。
invalidate()
该函数的作用是请求View树进行重绘,当应用程序需要重绘某个视图时,可以调用该函数。大致做了两件事情。
1、给所有需要重绘的视图添加了一个DIRTY或者DIRTY_OPAQUE标记。
2、通过矩形运算,找到真正需要重绘的矩形区,并将其保存在了ViewRoot类中的mDirty变量中。
requestFocus()
要想让某个视图获得焦点
1、用户使用方向键将焦点移动到该视图(其实也是调用requestFocus()函数完成)。
2、直接调用视图的requestFocus()函数。
下面分析requestFocus(direction, preFocusRect)的执行过程:
1、判断该视图是不是FOCUSABLE的,如果不是,则直接返回false。
2、如果当前是Touch模式,但是视图的FOCUSABLE_IN_TOUCH_MODE却为false,即该视图不能在Touch模式下获得焦点,则直接返回false。
3、调用hasAncestorThatBlockDescendantFocus()判断是否父视图阻止该子视图获得焦点,如果阻止,则直接返回false。应用程序可以调用ViewGoup的setDescendantFocusability(int focusability)方法设置该ViewGroup是否阻止其子视图获得焦点,默认情况下都不阻止。
4、以上三步实际上执行的都是前期检查,调用handleFocusGainInternal(dir, rect)进行具体的焦点获取操作,执行完该函数后,则该视图肯定获取焦点,所以返回true。
requestLayout()
该函数的执行过程比较简单,因为当View树进行重新布局时,总是重新给所有的视图进行布局,因为,最简单的想法就是只要设置一个标识就好。
首先给mPrivateFlags添加FORCE_LAYOUT标识,然后调用mParent的requestLayout()函数。
1、调用checkThread()确保本次调用是在UI线程中执行的,非UI线程执行该函数将导致状态管理的混乱,并最终crash掉。
2、给ViewRoot中的变量mLayoutRequested赋值为true,之后真正进行布局的代码将检查该变量,并决定是否需要重新布局。
3、调用scheduleTraversals()发起一个View树遍历的消息,该消息是异步处理的,对应的处理函数是performTraversals()。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值