五、View的工作原理

1.ViewRoot和DecorView

ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl和Decor-View建立关联。

DecorView作为顶级View,一般情况下,它的内部会包含一个竖直的LinearLayout,LinearLayout有上下两部分(具体看Android版本和当前的主题),上面是标题栏下面是内容栏。在Activity我们通过setContentView所设置的布局就是被添加到内容栏中的,而内容栏的id是content,布局是FrameLayout。
得到content:
findViewById(R.android.id.content);
得到我们设置的view:
findViewById(R.android.id.content).getChildAt(0);

DecorView是一个FrameLayout。

2.MeasureSpec

MeasureSpec代表一个32位的int值,高2位代表SpecMode,低30位代表SpecSize,SpecMode代表测量模式,而SpecSize是指在某种测量模式下的规格大小。

MeasureSpec的简单说明
http://blog.csdn.net/chunqiuwei/article/details/44515345

Java 位运算(移位、位与、或、异或、非)
http://blog.csdn.net/xiaochunyong/article/details/7748713

MeasureSpec的代码很少:

    public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
        public static final int EXACTLY     = 1 << MODE_SHIFT;
        public static final int AT_MOST     = 2 << MODE_SHIFT;
        public static int makeMeasureSpec( int size, int mode) {
            if ( sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return ( size & ~ MODE_MASK) | ( mode & MODE_MASK);
            }
        }
        public static int getMode(int measureSpec) {
            return ( measureSpec & MODE_MASK);
        }
        public static int getSize(int measureSpec) {
            return ( measureSpec & ~ MODE_MASK);
        }

        static int adjust(int measureSpec, int delta) {
            return makeMeasureSpec(getSize (measureSpec + delta ), getMode(measureSpec));
        }
    }

把SpecMode和SpecSize打包成int值,提供解包方法,方便操作。

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

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

AT_MOST
父容器指定了一个可用大小即SpecSize,view的大小不能大于这个值,具体是什么值,要看不同view的具体实现,它对应于LayoutParams中的warp_content。

表 普通view的MeasureSpec的创建规则:
普通view的MeasureSpec的创建规则
解释:当view采用固定宽高的时候,不管父容器的MeasureSpec是什么,view的MeasureSpec都是精确模式并且其大小遵循Layoutparams中的大小。

当view的宽高是match_parent的时候,如果父容器的模式是精确模式,那么view也是精确模式并且其大小不会超过父容器的剩余空间,如果父容器是最大模式,那么view也是最大模式并且大小不会超过父容器的剩余空间。

当view的宽高是warp_content时,不管父容器的模式是精确还是最大化,view的模式总是最大化并且大小不能超过父容器的剩余空间。

3.View的工作流程

View的工作流程主要是指measure,layout,draw这三大流程,分别测量宽高,确定位置,画出来。
Measure:
在Activity里面获取view的宽高:
(1)Activity/view的onWindowFocusChanged
这个方法的含义是:view已经初始化完毕了,宽高已经准备好了。需要注意的是这个方法会被调用多次,当Activity的窗口得到焦点和失去焦点时均会被调用,具体如果频繁的onResume和onPause
,那么就会多次调用。

      public void onWindowFocusChanged( boolean hasFocus) {
             super.onWindowFocusChanged( hasFocus);
             if( hasFocus) {
                   int width = tv.getMeasuredWidth();
                   int height = tv.getMeasuredHeight();
            }
      }

(2)view.post(Runnable)
通过post可以将一个runnable投递到消息队列的尾部,然后等待Looper调用此Runnable的时候,view也已经初始化好。

      protected void onStart() {
             super.onStart();
             tv.post( new Runnable() {                  
                   @Override
                   public void run() {
                int width = tv.getMeasuredWidth();
                int height = tv.getMeasuredHeight();

                  }
            });
      }

(3)ViewTreeObserver
使用ViewTreeObserver的众多回调可以完成这个功能,比如使用onGlobalLayoutListener这个接口,当view树的状态发生变化或者view树内部的view的可见性发生改变时,onGlobalLayout方法将被回调,因此这是获取view的宽高的一个很好的时机。
注意:随着view树状态的发生的变化,会被调用多次。

      protected void onStart() {
             super.onStart();
            ViewTreeObserver observer = mr.getViewTreeObserver();
             observer.addOnGlobalLayoutListener( new OnGlobalLayoutListener() {

                   @Override
                   public void onGlobalLayout() {
                         mr.getViewTreeObserver(). removeGlobalOnLayoutListener( this);
                int width = mr.getMeasuredWidth();
                int height = mr.getMeasuredHeight();

                  }
            });
      }

(4)view.measure(int widthMeasureSpec,int heightMeasureSpec )
需要根据view的LayoutParams来分:
mach_parent:无法Measure出来。
具体数值:

int widthMeasureSpec = MeasureSpec. makeMeasureSpec(100, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec. makeMeasureSpec(100, MeasureSpec.EXACTLY);
mr.measure( widthMeasureSpec, heightMeasureSpec);

warp_content:
如下Measure:

int widthMeasureSpec = MeasureSpec. makeMeasureSpec((1<<30)-1, MeasureSpec. AT_MOST);
int heightMeasureSpec = MeasureSpec. makeMeasureSpec((1<<30)-1, MeasureSpec. AT_MOST);
mr.measure( widthMeasureSpec, heightMeasureSpec);

用view在理论上能支持的最大的值去构造MeasureSpec是合理的。

两种错误的用法:

int widthMeasureSpec = MeasureSpec. makeMeasureSpec(-1, MeasureSpec.UNSPECIFIED );
int heightMeasureSpec = MeasureSpec. makeMeasureSpec(-1, MeasureSpec.UNSPECIFIED );
mr.measure( widthMeasureSpec, heightMeasureSpec);
mr.measure( LayoutParams.WRAP_CONTENT, LayoutParams. WRAP_CONTENT);

Layout:
View的getMeasureWidth和getWidth这两个方法有什么区别?
View的默认实现中,是相等的,只是赋值的时机不同,实际开发中可能会不相等。

public void layout(int l,int t,int r,int b) {
super.layout(l,t,r+100,b+100);
}

Draw:
view的绘制过程遵循下面几个步骤:
(1)绘制背景background.draw(canvas)
(2)绘制自己(onDraw)
(3)绘制children(dispatchDraw)
(4)绘制装饰(onDrawScrollBars)

4.自定义View
分类:
1.继承view,重写onDraw方法。
2.继承ViewGroup派生特殊的Layout。
3.继承特定的View如TextView。
4.继承特定的ViewGroup如LinearLayout。

须知:
1.让view支持warp_content
在onMeasure里面处理。

2.如有必要,让view支持padding
View要在onDraw方法中要处理padding,而ViewGroup要在onMeasure和onLayout中处理padding和margin。

3.尽量不要在view中使用HandlerView内部提供了post系列方法供使用。

4.View中如果有线程或者动画,需要及时停止在View的onDetachedFromWindow中停止动画,线程或回收其他资源。

5.View带有滑动嵌套的时候,需要处理好滑动冲突需要处理好滑动冲突。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值