笔记:View的工作原理

2017-10-23  本文已影响17人  jiaming_

View的工作原理


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

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


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



specMode:测量模式

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

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

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

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


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





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




  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位二进制表示)



getWidth()和getMesuredWidth()

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

(p196)

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


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

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

2.绘制自己(onDraw)

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

4.绘制装饰(onDrawScrollBars)


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

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


  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带有滑动嵌套时,需要处理好滑动嵌套


上一篇下一篇

猜你喜欢

热点阅读