美文网首页
Activity详解

Activity详解

作者: 二旬一月 | 来源:发表于2020-06-01 20:03 被阅读0次

    前言
    这是我深入一点学习Activity的学习过程,归纳总结了很多前辈的博客并非是原创,这里记录的只是我自己的学习过程,如果觉得不够清楚或者想要更深入的了解原作文章末尾会注明原作出处。

    一、Activity窗口的启动流程

    Activity启动流程图.jpeg
    想要看懂这个东西,首先需要了解的,就是各个部分代表的是什么,他们有什么作用:
    个人感觉很难,无从下手,但是提醒自己不要怕,暂时略过,先搞懂下面的,再回来一点点补充

    二、Activity的构成

    activity结构.png
    Activity源码.png
    这是一张源码重要方法的调用图,如果对各个方法做了什么不清楚的可以查看源码或者另一篇文几个重点方法的说明

    三、Activity的绘制

    绘制流程.png

    一个Activity的构成有,Activity、PhoneWindow、DecorView,再加上DecorView里面的TitleBar和我们填充的content内容,这些还Activity构成需要的具体类,不是一些辅助类,其实还有ViewRootImpl、WindowManagerImpl、WindowManagerGlobal、ActivityThread这几个不可见的辅助类,虽然是Activity构成可见UI界面没用到,但是这几个辅助类都是直接或者间接负责了Activity的构建和绘制的。下面我按照我的理解来一一介绍这些类的作用和被创建时机,有不对的地方还请大家指正:

    • Activity:一个继承ContextThemeWrapper的类,继承自ContextThemeWrapper那么意味着,通过Activity可以访问当前包的资源(getResources、getAssets)和启动其他组件(Activity、Service、Broadcast)以及得到各种服务(getSystemService),并且定义了当前Activity的Theme主题类型 ,关于Context的解释请看:http://www.cnblogs.com/android100/p/Android-Context.html
      并且只有创建了一个Activity才会创建后面的PhoneWindow、DecorView、ViewRootImpl、WindowManagerImpl、和Surface这些类。我这里并没说ActivityThread类,ActivityThread是Android应用的主线程(UI线程),一个应用进程才有一个UI线程。也没有说WindowManagerGlobal类,这个类用了几个数组管理一个应用进程内所有Activity的DecorView和ViewRootImpl以及WindowManager对应关系的。

    • WindowManagerGlobal是一个单例的类,一个应用进程内也只有一个。但是WindowManagerImpl是每一个Activity都有一个的。Activity是在ActivityThread的performLaunchActivity方法中用ClassLoader类加载器创建出来的。

    • PhoneWindow: 一个继承于抽象类Window的类,也是Window的唯一实现类。在Activity中PhoneWindow处在顶级位置,但是PhoneWindow是一个不可见的类,PhoneWindow内的DecorView才是我们可见的UI布局,但是我们可见的UI布局为啥要包装一层PhoneWindow列?PhoneWindow负责了Activity UI界面顶级布局DecorView的创建,PhoneWindow保存了window attributes即窗口布局属性参数,保存了与WindowManagerService进程通讯的IBinder (取名token)方便去系统WindowManagerService进程通讯,并且负责了用户Key和Touch事件的分发,但是PhoneWindow分发事件也是交给了DecorView来完成的。除了我们Activity在setContent时PhoneWindow创建了DecorView,好像PhoneWindow并没有负责太多的Render的事件。看源码确实就是如此啊!,其实真正的Render操作都在ViewRootImpl类中。PhoneWindow是在Activity的attach方法中new出来的。

    • WindowManagerImpl: 见名知意,就是管理Window的,在这里就是管理PhoneWindow的,每一个Activity都有一个自己的WindowManagerImpl来管理自己的PhoneWindow,WindowManagerImpl其实是用来管理PhoneWindow里面的DecorView的,也可能是DecorView级别的其他的View,还有就是WindowManagerImpl是用来跟一个App全局的PhoneWindow管理器WindowManagerGlobal通信的,添加、移除和更新DecorView层级的View时WindowManagerImpl会调用WindowManagerGlobal中的方法来进行全局管理。WindowManagerImpl是在Activity的attach方法中new PhoneWindow后调用mWindow.setWindowManager方法创建的,其内部是调用setWindowManager的createLocalWindowManager方法new出来的。

    • DecorView: 是一个ViewGroup,继承自FrameLayout,是我们看到的UI界面的顶级容器,DecorView是PhoneWindow的成员。DecorView除了是Activity界面的顶级容器以外,DecorView还是key和touch事件从上向下真正分发开始的源头。事件分发从Activity—》PhoneWindow—》DecorView—》层层向下分发到我们的界面底层。DecorView其实在很多地方都会创建,PhoneWindow内部的getDecorView方法被调用时,要是内部的成员变量mDecor为空的话,就会调用installDecor()方法去创建一个DecorView,最后new 一个DecorView返回,保证调用PhoneWindow的getDecorView获取 DecorView时永远不会为空。我看代码发现PhoneWindow的DecorView第一次调用发生在ActivityThread内的handleResumeActivity方法中,handleResumeActivity方法中还有一个很关键的步骤,就是调用了WindowManagerImpl的addView(DecorView,WindowManager.LayoutParams )方法,进去创建了一个跟DecorView关联的ViewRootImpl类。

    • ViewRootImpl:是一个实现了ViewParent接口的final类。我前面说到了,ViewRootImpl主要是负责Activity的界面Render绘制的,负责整个窗口界面的ViewTree的绘制更新。ViewRootImpl要负责DecorView的绘制,那么ViewRootImpl就需要持有当前Activity的DecorView的引用,是的,的确如此,前面介绍DecorView的创建的时候我说到了DecorView第一次创建发生在ActivityThread内的handleResumeActivity方法中,handleResumeActivity方法中还有一个很关键的步骤,就是调用了WindowManagerImpl的addView(DecorView,WindowManager.LayoutParams )方法,进去创建了一个跟DecorView关联的ViewRootImpl类。WindowManagerImpl的addView方法其实调用了WindowManagerGlobal的addView方法,在这个方法中创建了ViewRootImpl,并且在这个方法中调用了ViewRootImpl的setView方法,使ViewRootImpl持有DecorView的引用。并且把实现了ViewParent接口的自己设置成了DecorView的Parent。这样ViewRootImpl就可以肆无忌惮地操作DecorView了。其实我在调用普通view的invalidate()刷新时,其实是经过层层Parent寻找,最终调用了最顶层的ParentViewRootImpl的invalidate()来判断和处理每次的UI界面刷新的。

    • Surface:一个实现了Parcelable接口的类,对Parcelable接口不了解的,请原作Binder通信的文章:Android IPC之AIDL看这一篇还不够
      。Surface是原始图像缓冲区(raw buffer)的一个句柄,而原始图像缓冲区是由屏幕图像合成器(screen compositor)管理的。得到了Surface这个句柄就可以得到其中的Canvas、原生缓冲器以及其它方面的内容。所以说Surface是生成Canvas的地方,具体就是调用lockCanvas方法获得Canvas。至于Canvas、Paint 和Bitmap的关系我这里就不讲了,后面会有文章来讲这个。Surface是ViewRootImpl的一个final成员变量,伴随ViewRootImpl的创建默认就new一个出来了,但是此时的Surface是一个空的,里面是没有内容的。Surface的数据填充是要跟WindowManagerService交互的,应用进程端Binder通知WindowManagerService进程端创建一个Surface对象,最后是将WindowManagerService进程端的 Surface对象传递到应用进程端并赋值给应用进程的Surface对象,这样窗口可以使用Surface来绘制UI了。因为Surface对象要夸进程传递,所以Surface要实现Parcelable接口。更详细的介绍关于窗口的Surface创建请看老罗的:Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析

      Surface是ViewRootImpl的一个final成员变量,伴随ViewRootImpl的创建默认就new一个出来了,但是此时的Surface是一个空的,里面是没有内容的。Surface的数据填充是要跟WindowManagerService交互的,应用进程端Binder通知WindowManagerService进程端创建一个Surface对象,最后是将WindowManagerService进程端的 Surface对象传递到应用进程端并赋值给应用进程的Surface对象,这样窗口可以使用Surface来绘制UI了。因为Surface对象要夸进程传递,所以Surface要实现Parcelable接口。

    
    
    /**1*/ ApplicationThread的onTransact方法接收到SystemServer进程的SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION启动Activity的Binder信息
                    ↓
    /**2*/ ApplicationThread.scheduleLaunchActivity() //
                    ↓
    /**3*/ ActivityThread.scheduleLaunchActivity() //安排启动Activity
                    ↓
    /**4*/ ActivityThread.handleLaunchActivity()  //处理启动Activity
                    ↓
    /**5*/ ActivityThread.handleResumeActivity() // Activity 的Resume会使DecorView跟ViewRootImpl关联
                    ↓
    /**6*/ WindowManagerGlobal.addView() //全局保存窗口的信息
                    ↓
    /**7*/ ViewRootImpl.setView()  //使DecorView和ViewRootImpl关联并绘制界面
                    ↓
    /**8*/ ViewRootImpl.requestLayout() //请求绘制ViewTree
                    ↓
    /**9*/ ViewRootImpl.scheduleTraversals() // 安排遍历 
                    ↓
    /**10*/ ViewRootImpl.doTraversal() //
                    ↓
    /**11*/ ViewRootImpl.performTraversals() //执行遍历 会根据情况调用relayoutWindow performMeasure performLayout performDraw 等方法 这四个方法跟绘制是紧密相关的
                    ↓
    /**12*/ ViewRootImpl.relayoutWindow() //窗口第一次创建或者是窗口大小有变化并且窗口可见就会调用此方法 
                    ↓
    /**13*/ ViewRootImpl.mWindowSession.relayout() //binder通信通知WindowManagerService创建一个跟应用端关联的Surface
                    ↓
    /**14*/ ViewRootImpl调用performMeasure performLayout performDraw方法绘制UI
    
    

    其实第五步ActivityThread.handleResumeActivity()方法内会调用到WindowManagerGlobal.addView()方法把窗口绘制完成,紧接着就调用到了activity.makeVisible()方法,其实就是显示出DecorView,因为DecorView创建绘制前是被设置成了INVISIBLE的,Activity中的makeVisible方法就是把绘制完成的DecorView显示出来了

        void makeVisible() {
            if (!mWindowAdded) {
                ViewManager wm = getWindowManager();
                wm.addView(mDecor, getWindow().getAttributes());
                mWindowAdded = true;
            }
            mDecor.setVisibility(View.VISIBLE); //把DecorView设置成可见的
        }
    

    补充:
    Activity窗口组件之间的对应关系
    一个设备有一个WindowManagerService进程 有一个SystemServer进程
    一个App有一个WindowManagerGlobal类 有一个ActivityThread类 有一个ApplicationThread类
    一个App可以有很多个Activity
    一个 Activity有一个PhoneWindow类
    一个PhoneWindow类有一个ViewRootImpl类
    一个PhoneWindow类有一个WindowManagerImpl类
    一个ViewRootImpl类有一个Surface类 有一个DecorView类

    每一个应用程序窗口都对应有两个Java层的Surface对象,其中一个是在WindowManagerService服务这一侧创建的,而另外一个是在应用程序进程这一侧创建的。

    在WindowManagerService服务这一侧创建的Java层的Surface对象在C++层关联有一个SurfaceControl对象,用来设置应用窗口的属性,例如,大小和位置等。

    在应用程序进程这一侧创建的ava层的Surface对象在C++层关联有一个Surface对象,用来绘制应用程序窗品的UI。
    原文链接:https://blog.csdn.net/u010072711/article/details/78225994
    两张图出自出自网易云课堂,高级安卓aUI教学视频

    三大步

    测量

    • 关于测量首相需要了解的是MeasureSpec,他是View的一个静态内部类,具体源码如下:
    public static class MeasureSpec {
            private static final int MODE_SHIFT = 30;
            private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
    
            /** @hide */
            @IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
            @Retention(RetentionPolicy.SOURCE)
            public @interface MeasureSpecMode {}
    
            /**
             * Measure specification mode: The parent has not imposed any constraint
             * on the child. It can be whatever size it wants.
             * 父级没有对子级施加任何约束,它可以是它想要的大小,一般是系统调用
             * 00左移30位:00000000000000000000000000000000
             */
            public static final int UNSPECIFIED = 0 << MODE_SHIFT;
    
            /**
             * Measure specification mode: The parent has determined an exact size
             * for the child. The child is going to be given those bounds regardless
             * of how big it wants to be.
             * 父控件已经为子控件确定了一个确切的大小,不管子控件想要多大,都会得到这些界限
             * 01左移30位:01000000000000000000000000000000
             */
            public static final int EXACTLY     = 1 << MODE_SHIFT;
    
            /**
             * Measure specification mode: The child can be as large as it wants up
             * to the specified size.子级可以是它想要的大小
             * 11左移30位:11000000000000000000000000000000
             */
            public static final int AT_MOST     = 2 << MODE_SHIFT;
            //从二进制我们可以看出,不一样的只有前两位
            /**
             * Creates a measure specification based on the supplied size and mode.
             * 模式和具体尺寸进行打包,因为sUseBrokenMakeMeasureSpec这个参数是false
             * 所以真正的打包计算是else中的过程,结果就是前两位表示测量模式,后面三十位表示具体尺寸
             */
            public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                              @MeasureSpecMode int mode) {
                if (sUseBrokenMakeMeasureSpec) {
                    return size + mode;
                } else {
                    return (size & ~MODE_MASK) | (mode & MODE_MASK);
                }
            }
    
            /**
             * Like {@link #makeMeasureSpec(int, int)}, but any spec with a mode of UNSPECIFIED
             * will automatically get a size of 0. Older apps expect this.
             *
             * @hide internal use only for compatibility with system widgets and older apps
             */
            public static int makeSafeMeasureSpec(int size, int mode) {
                if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
                    return 0;
                }
                return makeMeasureSpec(size, mode);
            }
    
    测量顶层view.png
    • 了解MeasureSpec之后再去看具体绘制过程,
      在viewRootImpl中我们找到以下这段代码
    if (!mStopped || mReportNextDraw) {
                    boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                            (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                    if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                            || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                            updatedConfiguration) {
                        //1.根据窗口的大小和 view 本身的宽高打包生成MeasureSpec        
                        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    
                        if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed!  mWidth="
                                + mWidth + " measuredWidth=" + host.getMeasuredWidth()
                                + " mHeight=" + mHeight
                                + " measuredHeight=" + host.getMeasuredHeight()
                                + " coveredInsetsChanged=" + contentInsetsChanged);
    
                         // Ask host how big it wants to be
                        //跳转到performMeasure进行绘制
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    
                        // Implementation of weights from WindowManager.LayoutParams
                        // We just grow the dimensions as needed and re-measure if
                        // needs be
                        int width = host.getMeasuredWidth();
                        int height = host.getMeasuredHeight();
                        boolean measureAgain = false;
    
                        if (lp.horizontalWeight > 0.0f) {
                            width += (int) ((mWidth - width) * lp.horizontalWeight);
                            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
                                    MeasureSpec.EXACTLY);
                            measureAgain = true;
                        }
                        if (lp.verticalWeight > 0.0f) {
                            height += (int) ((mHeight - height) * lp.verticalWeight);
                            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                                    MeasureSpec.EXACTLY);
                            measureAgain = true;
                        }
    
                        if (measureAgain) {
                            if (DEBUG_LAYOUT) Log.v(mTag,
                                    "And hey let's measure once more: width=" + width
                                    + " height=" + height);
                            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                        }
    
                        layoutRequested = true;
                    }
                }
    

    getRootMeasureSpec方法的源码,从中我们可以看出对应关系
    MATCH_PARENT--> EXACTLY
    WRAP_CONTENT--> AT_MOST
    自定义大小--> EXACTLY

    private static int getRootMeasureSpec(int windowSize, int rootDimension) {
            int measureSpec;
            switch (rootDimension) {
    
            case ViewGroup.LayoutParams.MATCH_PARENT:
                // Window can't resize. Force root view to be windowSize.
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
                break;
            case ViewGroup.LayoutParams.WRAP_CONTENT:
                // Window can resize. Set max size for root view.
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
                break;
            default:
                // Window wants to be an exact size. Force root view to be that size.
                measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
                break;
            }
            return measureSpec;
        }
    

    跳转到performMeasure方法的实现后我们会看到这样一句话mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    这里的mView此时所指的是decorView,decorview是FrameLayout的子类,所以我们接下来应该去FrameLayout中查看measure方法

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int count = getChildCount();
    
            final boolean measureMatchParentChildren =
                    MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                    MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
            mMatchParentChildren.clear();
    
            int maxHeight = 0;
            int maxWidth = 0;
            int childState = 0;
            //for循环遍历子view测量子view
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                if (mMeasureAllChildren || child.getVisibility() != GONE) {
                    // 测量子view
                    measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    maxWidth = Math.max(maxWidth,
                            child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                    maxHeight = Math.max(maxHeight,
                            child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                    childState = combineMeasuredStates(childState, child.getMeasuredState());
                    if (measureMatchParentChildren) {
                        if (lp.width == LayoutParams.MATCH_PARENT ||
                                lp.height == LayoutParams.MATCH_PARENT) {
                            mMatchParentChildren.add(child);
                        }
                    }
                }
            }
    

    继续往里看measureChildWithMargins,发现子view的测量需要parentWidthMeasureSpec,结合上面的额for循环,也就形成了递归的模式去测量子view。

    protected void measureChildWithMargins(View child,
                int parentWidthMeasureSpec, int widthUsed,
                int parentHeightMeasureSpec, int heightUsed) {
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
    
            final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                    mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                            + widthUsed, lp.width);
            final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                    mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                            + heightUsed, lp.height);
    
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    

    下一步看getChildMeasureSpec是如何实现的

    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) {
            // Parent has imposed an exact size on us
            case MeasureSpec.EXACTLY:
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    // Child wants to be our size. So be it.
                    resultSize = size;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                }
                break;
    
            // Parent has imposed a maximum size on us
            case MeasureSpec.AT_MOST:
                if (childDimension >= 0) {
                    // Child wants a specific size... so be it
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    // Child wants to be our size, but our size is not fixed.
                    // Constrain child to not be bigger than us.
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    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 = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = MeasureSpec.UNSPECIFIED;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size.... find out how
                    // big it should be
                    resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = MeasureSpec.UNSPECIFIED;
                }
                break;
            }
            //noinspection ResourceType
            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
        }
    

    这里我们发现DecorView和子View的MeasureSpec打包过程是不一样的。
    下一步,回到FrameLayout中的onMeasure方法继续往下看,准备好MeasureSpec之后又进行了一系列操作,调用了setMeasuredDimension完成测量。
    到此,测量过程告一段落,总结如下

        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        --》
        // 这里的mView是DecorView,也就是要去FrameLayout中查看measure
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        --》
        measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                    mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                            + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                    mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                            + heightUsed, lp.height);
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    

    顶层view测量规格计算方式
    MATCH_PARENT --> EXACTLY
    WRAP_CONTENT --> AT_MOST
    自定义大小 --> EXACTLY
    普通view测量规格计算方式


    view的测量.png

    方法调用层级

    • ViewGroup:measure -》onMeasure(测量子控件的宽高)-》setMeasuredDimens-》setMeasuredDimensionRaw(保存自己的宽高)
    • View:measure -》setMeasuredDimens-》setMeasuredDimensionRaw(保存自己的宽高)
      说明:在自定义控件是ViewGroup的时候,需要多一步,测量子控件宽高。
    public static int getDefaultSize(int size, int measureSpec) {
            int result = size;
            int specMode = MeasureSpec.getMode(measureSpec);
            int specSize = MeasureSpec.getSize(measureSpec);
    
            switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = size;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            }
            return result;
        }
    

    在view 的测量过程中用到的方法getDefaultSize方法说明,AT_MOST和EXACTLY返回结果都是一样的,所以我们在自定义view 的时候,如果不重写onMeasure方法,那match_parent和 wrap_content 他们的效果是一样的。

    布局

    view的布局.png

    这里同样使用到了递归

    绘制

    view的绘制.png
    参考:
    https://blog.csdn.net/u010072711/article/details/78225994
    https://www.bilibili.com/video/BV1xE411j7AB?p=3

    相关文章

      网友评论

          本文标题:Activity详解

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