自定义view流程

109 阅读4分钟

MeasureSpec

测量规格,封装了父容器对 view 的布局上的限制,内部提供了宽高的信息(SpecMode、SpecSize),SpecSize是指在某种SpecMode下的参考尺寸,其中SpecMode 有如下三种:

  • UNSPECIFIED 父控件不对你有任何限制,你想要多大给你多大,想上天就上天。这种情况一般用于系统内部,表示一种测量状态。(这个模式主要用于系统内部多次Measure的情形,并不是真的说你想要多大最后就真有多大)

  • EXACTLY 父控件已经知道你所需的精确大小,你的最终大小应该就是这么大。

  • AT_MOST 你的大小不能大于父控件给你指定的size,但具体是多少,得看你自己的实现。

    /** *

    • 目标是将父控件的测量规格和child view的布局参数LayoutParams相结合,得到一个

    • 最可能符合条件的child view的测量规格。

    • @param spec 父控件的测量规格

    • @param padding 父控件里已经占用的大小

    • @param childDimension child view布局LayoutParams里的尺寸

    • @return child view 的测量规格 */ public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); //父控件的测量模式 int specSize = MeasureSpec.getSize(spec); //父控件的测量大小

      int size = Math.max(0, specSize - padding);

      int resultSize = 0; int resultMode = 0;

      switch (specMode) { // 当父控件的测量模式 是 精确模式. case MeasureSpec.EXACTLY: //如果child的布局参数有固定值,比如"layout_width" = "100dp" //那么显然child的测量规格也可以确定下来了,测量大小就是100dp,测量模式也是EXACTLY if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; }

       //如果child的布局参数是"match_parent",也就是想要占满父控件
       //而此时父控件是精确模式,也就是能确定自己的尺寸了,那child也能确定自己大小了
       else if (childDimension == LayoutParams.MATCH_PARENT) {
           resultSize = size;
           resultMode = MeasureSpec.EXACTLY;
       }
       //如果child的布局参数是"wrap_content",也就是想要根据自己的逻辑决定自己大小,
       //比如TextView根据设置的字符串大小来决定自己的大小
       //那就自己决定呗,不过你的大小肯定不能大于父控件的大小嘛
       //所以测量模式就是AT_MOST,测量大小就是父控件的size
       else if (childDimension == LayoutParams.WRAP_CONTENT) {
           resultSize = size;
           resultMode = MeasureSpec.AT_MOST;
       }
       break;
      

      // 当父控件的测量模式 是 最大模式,也就是说父控件自己还不知道自己的尺寸,但是大小不能超过size case MeasureSpec.AT_MOST: //同样的,既然child能确定自己大小,尽管父控件自己还不知道自己大小,也优先满足孩子的需求 if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } //child想要和父控件一样大,但父控件自己也不确定自己大小,所以child也无法确定自己大小 //但同样的,child的尺寸上限也是父控件的尺寸上限size else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } //child想要根据自己逻辑决定大小,那就自己决定呗 else if (childDimension == LayoutParams.WRAP_CONTENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } break;

      // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }

基本概念以及流程      可以看到,官方的文档只是泛泛的说了一下,没有具体详细的说明整个流程,然后在网上找了一些资料,结合官方文档来详细说了下,对于理解这个部分,效果可能会更好。在正式介绍之前,还是需要理解与UI相关的基本概念。       Activity:Activity包含一个Window,该Window在Activity的attach方法中通过调用PolicyManager.makeNewWindo创建;      View:最基本的UI组件,表示屏幕上的一个矩形区域;

DecorView:是Window中View的RootView,设置窗口属性;

Window:表示顶层窗口,管理界面的显示和事件的响应;每个Activity 均会创建一个 PhoneWindow对象,是Activity和整个View系统交互的接口

WindowManager:一个interface,继承自ViewManager。所在应用进程的窗口管理器;有一个implementation WindowManagerImpl;主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。

ViewRoot:通过IWindowSession接口与全局窗口管理器进行交互:界面控制和消息响应;

ActivityThread:应用程序的主线程,其中会创建关联当前Activity与Window;创建WIndowManager实现类实例,把当前DecoView加入到WindowManager;