美文网首页
笔记:View的工作原理

笔记:View的工作原理

作者: jiaming_ | 来源:发表于2017-10-23 09:28 被阅读17次

    View的工作原理


    • ViewRoot 和DecorView

    ViewRoot对应于ViewRootImpl类,它是连接DecorView和WindowManager的纽带,View的三大绘制流程都是通过ViewRoot来完成。

    在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl,并将ViewRootImpl对象和DecorView对象关联。


    Activity中View的绘制流程是从ViewRoot的perfromTraversals()方法开始的,它经过measure layout draw 最终将View绘制出来



    • MeasureSpec 代表32位int值,高2位代表specMode,低30位代表specSize

    specMode:测量模式

    specSize:在某种测量模式下的规格大小

    • SpecMode 类型

    UNSPECIFIED: 父容器不对View限制,要多大有多大,这种情况一般用于系统内部,表示一种测量状态。

    EXACTLY: 父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值,它对应于LayoutParams中的Match_parent和具体的值。

    AT_MOST: 父容器指定了一个可视大小,即SpecSize,View的大小不能大于这个值,具体要看不同View的具体实现,对应于wrap_content


    • ******父容器和LayoutParams一起决定View的MeasureSpec

    系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后再根据MeasurSpec来确定View测量后的宽高。


    • 父容器测量子View P181


    • DecorView对MeasureSpec的转换测量有点差别,其MeasureSpec值由窗口尺寸和其自身的LayoutParams来决定


    • View 的mesure方法会调用onMeasure方法,所有只需要实现View的onMeasure就可以了。

    View的measure方法又会被所在的ViewGroup的measureChild方法调用。measureChild方法会遍历child,执行child的measure。

    • 在onMeasure中又调用了setMeasureDimension()方法设置View宽高的测量值。

    • 在measure完以后,通过getMeasuredWidth/Height()可获得测量后的宽/高

    • View的Measure过程和Activity的生命周期过程不同步,

    • 如何在Activity中获取View的宽/高?

    1. Activity/View #onWindowFouceChanged()方法,表示View已经初始化完毕,宽高已经准备好

      但该方法会在Acitivty焦点获取或失去时都会调用,也就是onResume和onPause都会调用。

    2. view.post(runnable)

      view初始化完毕会执行runnable,在run()中view.getMesuredWidth()/height

    3. ViewTreeObserver

      当View树状态发生改变,或者View树内部的View发生改变,onGlobalLayout方法会被调用,在这里获取View的宽高。

        @Override
        protected void onStart() {
            super.onStart();
            ViewTreeObserver viewTreeObserver = vMarqueeView.getViewTreeObserver();
            viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    vMarqueeView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    int width = vMarqueeView.getMeasuredWidth();
                    int height = vMarqueeView.getMeasuredHeight();
                }
            });
        }
    
    1. view.measure(int widthMesureSpec,int heightMesureSpec) p192

    MATCH_PARENT:不可用,因为不知道父容器的尺寸

    WRAP_CONTENT:

            int widthMesureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
            int heightMesureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
            vMarqueeView.measure(widthMesureSpec, heightMesureSpec);
    

    (1<<30)-1 代表(2^30-1) 即使用最大值去构造MeasureSpec(View的尺寸使用30位二进制表示)


    • layout过程和mearsure过程一样,调用父元素的layout方法,layout方法调用onLayout方法,onlayout方法又会遍历子元素,调用子元素的layout,子元素layout又会调用onLayout,如此循环

    • View的测量宽高和最终宽高的区别:

    getWidth()和getMesuredWidth()

    本质上是相等的,只是,getWidht()值mWitdh形成于onLayout之后,而getMesuredWidth()的值形成于mesured之后

    (p196)

    注意:如过在onLayout中改变了l,r,t,b中某个值,则会影响到getWidth/getHeight,而不会影响getMesuredWidth/Height值


    • draw过程有点差别,但大致相同

    调用draw方法,draw方法会执行如下几步

    1.绘制背景(background.draw(cavans))

    2.绘制自己(onDraw)

    3.绘制children(dispatchDraw(),dispatchDraw又会循环child执行其draw()方法)

    4.绘制装饰(onDrawScrollBars)


    • View的setWillNotDraw(boolean willNotDraw) 优化

    如果View不做任何绘制内容时,那么设置这个标志位位true后,系统会进行相应的优化,默认情况下,View没有启用这个标志位,但ViewGroup会启用

    当自定义View继承ViewGroup且本身不具备绘制功能时,可以开启这个标志位,从而便于系统的后续优化,当明确知道继承ViewGroup的View需要通过onDraw绘制相应内容时,需要显示的关闭这个标志位


    • 绘制自定义View须知: p201
    1. 让View支持wrap_content

    直接继承View或ViewGroup的控件,如果不在onMeasure中对wrap_content做特殊处理,那么在外界布局中使用wrap_content就无法达到预期效果。

    1. 如果有必要,让View支持padding

    直接继承View的控件,如果不在draw方法中处理padding,那么padding属性无法起作用。另外,直接继承ViewGroup的控件要在其onMeasure和onLayout中要考虑padding和子元素margin对其造成的影响,不然将导致padding和子元素的margin失效

    1. 尽量不要在View中使用Handler

    View内部本身提供了post方法

    1. 如果View中有线程或者动画,需及时停止,参考View#onDetachedFromWindow

    当Activity退出或者当前View被remove后,View的onDetachedFromWindow将会被调用,对应的方法是onAttachedToWindow(),该方法在onDraw之前任何时候调用,不确定在onMesure之前还是之后调用,当包含该View的Activity被启动时调用。

    1. View带有滑动嵌套时,需要处理好滑动嵌套

    • 再次强调,自定义组件继承View或ViewGroup的,padding值需要自己处理,在onDraw中处理padding,而margin值由父类处理,对于wrap_content,默认是按照match_parent处理,所以也需要自己处理wrap_content情况,设置个最小值。

    • 自定义属性 p206

    相关文章

      网友评论

          本文标题:笔记:View的工作原理

          本文链接:https://www.haomeiwen.com/subject/xgyouxtx.html