美文网首页
View绘制流程解析

View绘制流程解析

作者: 一行代码 | 来源:发表于2019-05-24 14:09 被阅读0次

一、目录

image

二、简介

在理解View绘制流程,之前我们先解析下View绘制中的几大重要成员。
  • Window

    • 介绍:Window表示一个窗口,用来承载View的抽象类。不管是Activity、Dialog还是Toast,它们的视图实际上都是附加在Window上的,因此Window实际是View的直接管理者。他的唯一实现是PhoneWindow
    image
  • WindowManager

    • 简介:WindowManager继承自ViewManager的接口。WindowManager主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。
    image
  • ViewRootImpl

    • 简介:ViewRootImpl是View中的最高层级,属于所有View的根(但ViewRootImpl不是View,只是实现了ViewParent接口),是实现 View 的绘制的类,它实现了View和WindowManager之间的通信协议。大多数情况下,他也是WindowManagerGloable的内部功能具体实现
  • DecorView

    • DecorView是Activity内所有View的父布局,DecorView 是 FrameLayout 的子类,FrameLayout 是 GroupView 的子类,所以DecorView 是一个 GroupView。内部包含上面是标题,下面是内容栏(id是content)

三、Activity、PhoneWindow、DecorView三者之间的关系

每一个Acyiviy 包含一个Window对象,这个window对象是phoneWindow
PhoneWindow内包含一个DecorView。同时,PhoneWindow也是Activity和View系统交互的接口。DecorView是整个窗口内的所有View的跟布局
DecorView将屏幕划分两个区域:1.titleView 2.ContentView
DecorView本质上是一个FrameLayout,是Activity中所有View的祖先
而我们平时所写的就是展示在ContentView中的,下图表示Activity的构成。
image

四、DecorView的加载

从Activity的startActivity开始,最终调用到ActivityThread的handleLaunchActivity方法来创建Activity,相关核心代码如下:
  • handleLaunchActivity
handleLaunchActivity的主要操作
1、初始化WindowManagerService
2、实例化Activity、初始化Activity的Window等参数、回调Activity的onCreate()
3、执行Acttivity的onResume()
 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        ...略
        // 初始化WindowManagerService
        WindowManagerGlobal.initialize();
        //实例化Activity,初始化Activity的Window等参数并回调Activity的onCreate、onStart
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            //执行Activity的onResume
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
        ...略            
        } else {
            ...略
        }
    }
  • performLaunchActivity
performLaunchActivity的操作步骤:
1、获取Activity信息
2、通过反射获取Activity的类,并创建Activity
4、获取Application
5、初始化Activity的成员变量包括Window、Application、和mainThread等。初始化PhoneWindow的构造函数的内部初始化了PhoneWindow的DecorView。
6、回调Activity的onCreate方法
    通常Activity的onCreate的内我们会调用setContentView
    而setContentView 内部调用PhoneWindow的setContentView,
    PhoneWindow的setContentView通过LayoutInflater.inflate()将我们的布局加载到DecorView中
7、如果Activity没有Finish则调用Activity的onStart()
8、设置Activity的savedInstanceState
9、设置Window的title
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       
        //1、获取Activity信息
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

       ...略
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            //2、通过反射获取到activity类,并创建Activity实例
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            //3、如果没在清单里注册就会出现这个错误
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            //4、获取Application
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                ...略
                appContext.setOuterContext(activity);
                //5、初始化Activity的成员变量包括Window、Application、和mainThread等
                //         初始化PhoneWindow的构造函数的内部初始化了PhoneWindow的DecorView。

                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                //6、回调Activity的onCreate方法
                // 通常Activity的onCreate的内我们会调用setContentView
                // 而setContentView 内部调用PhoneWindow的setContentView,
                // PhoneWindow的setContentView通过LayoutInflater.inflate()将我们的布局加载到DecorView中
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
                r.stopped = true;
                //7、如果Activity没有Finish则调用Activity的onStart()
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                // 8、设置Activity的savedInstanceState
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
                //9、设置Window的title
                if (!r.activity.mFinished) {
                    activity.mCalled = false;
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onPostCreate()");
                    }
                }
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }
  • handleResumeActivity
handleResumeActivity操作步骤:
1、执行Activity的onResume
2、调用 WindowManager的addView(decor, l);
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;
    // 调用Activity的onResume方法
    ActivityClientRecord r = performResumeActivity(token, clearHide);
    if (r != null) {
        final Activity a = r.activity;
        ...
        if (r.window == null &&& !a.mFinished && willBeVisible) {
            //获取Activity的Window
            r.window = r.activity.getWindow();
            //获取Window的DecorView
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            //获取Activity的WindowManagerImpl
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            //将Window的DecorView赋值个Activity的mDecor
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (a.mVisibleFromClient) {
                a.mWindowAdded = true;
                // WindowManager的实现类是WindowManagerImpl,
                // 所以实际调用的是WindowManagerImpl的addView方法
                wm.addView(decor, l);
            }
        }
    }
}
  • WindowManager的addView(View view, ViewGroup.LayoutParams params)

实际上WindowManager的实现类时WindowManagerImpl,而WindowManagerImpl的addView()方法内部,调用了WindowManagerGlobal的addView。所以接下来分析WindowManagerGlobal的addView。源码如下:

// WindowManagerGlobal的addView方法
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
    ...
    ViewRootImpl root;
    View pannelParentView = null;
    synchronized (mLock) {
        ...
        // 创建ViewRootImpl实例
        root = new ViewRootImpl(view..getContext(), display);
        view.setLayoutParams(wparams);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }
    try {
        // 把DecorView加载到Window中,触发View的测量、布局、绘制
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
        synchronized (mLock) {
            final int index = findViewLocked(view, false);
            if (index >= 0) {
                removeViewLocked(index, true);
            }
        }
        throw e;
    }
}
// ViewRootImpl的方法
 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
        ...
            requestLayout();        
        ...
            
        }
    }

public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
           ...
            scheduleTraversals();
        }
    }
 void scheduleTraversals() {
        if (!mTraversalScheduled) {
            ...
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
           ...
        }
    }
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
 void doTraversal() {
        if (mTraversalScheduled) {
            ...
            performTraversals();
            ...
        }
    }

最终ViewRootImpl的setView方法利用执行Runable的方式执行了performTraversals();

  • DecorView加载程序流执行程图
image

五、View绘制整体流程

View的绘制最终是从ViewRootImpl的performTraversals()方法开始,从上到下遍历整个视图树,每个View控件负责绘制自己,而ViewGroup还需要负责通知自己的子View进行绘制操作。ViewRootImpl的performTraversals()方法的主要源码如下:
private void performTraversals() {
    ...
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    ...
    //执行测量流程
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
    //执行布局流程
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    ...
    //执行绘制流程
    performDraw();
}

流程图如下:


image
  • 注:preformLayout和performDraw的传递流程和performMeasure是类似的,唯一不同的是,performDraw的传递过程是在draw方法中通过dispatchDraw来实现的,不过这并没有本质区别。
  • 获取content:
ViewGroup content = (ViewGroup)findViewById(android.R.id.content);

六、MeasureSpec

MeasureSpec是View类的一个静态内部类,用来说明应该如何测量这个View。MeasureSpec代表一个32位的int值,前俩位代表SpecMode,后30位代表SpecSize.
其中:SpecMode代表测量的模式,SpecSize值在某种测量模式下的规格大小。
  • 三个测量模式说明

    • EXACTLY:英文含义是精确的。精确测量模式,这时候View的大小是SpecSize的大小。对应布局参数是 MATCH_PARENT或精确值大小

    • AT_MOST:英文含义是至多。最大值测量模式,View的尺寸是不超过父布局最大尺寸的任意尺寸。对应布局参数wrap_content。

    • UNSPECIFIED:英文含义未指定的。不指定测量模式,父视图没有限制子视图的大小,子视图可以是想要的任何尺寸,通常用于系统内部,应用开发中很少用到。

  • 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;

    // 根据指定的大小和模式创建一个MeasureSpec
    public static int makeMeasureSpec(int size, int mode) {
        if (sUseBrokenMakeMeasureSpec) {
            return size + mode;
        } else {
            return (size & ~MODE_MASK) | (mode & MODE_MASK);
        }
    }
    //当 mode == UNSPECIFIED 时返回0的MeasureSpec,内部调用makeMeasureSpec,
    public static int makeSafeMeasureSpec(int size, int mode) {
            if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
                return 0;
            }
            return makeMeasureSpec(size, mode);
        }
    //获取测量模式
    public static int getMode(int measureSpec) {
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
    }

    获取测量大小
    public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
    }

    // 微调某个MeasureSpec的大小
    static int adjust(int measureSpec, int delta) {
        final int mode = getMode(measureSpec);
        if (mode == UNSPECIFIED) {
            // No need to adjust size for UNSPECIFIED mode.
            return makeMeasureSpec(0, UNSPECIFIED);
        }
        int size = getSize(measureSpec) + delta;
        if (size < 0) {
            size = 0;
        }
        return makeMeasureSpec(size, mode);
    }
}
  • DecorView的MeasureSpec的创建
DecorView的MeasureSpec的创建是在ViewRootImpl在测量DecorView之前获取的的。获取源码如下:
performTraversals(){
    ...
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    ...
    
}

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;
    }

总结:DecorView的MeasureSpec是由窗口大小和其自身的LayoutParams共同决定

  • 普通View的MeasureSpec的创建
普通View的MeasureSpec的创建是在父View在测量子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);
    }
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        //父View获取自身的测量模式和大小
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);
        //获取在父View的大小中除了自身padding、margin的大小
        int size = Math.max(0, specSize - padding);
    
        int resultSize = 0;
        int resultMode = 0;
        
        switch (specMode) {
        // 如何父布局大小是精确的大小或者是MATCH_PARENT
        case MeasureSpec.EXACTLY:
            //如果子View自身的LayoutParam的大小>0
            //则子View大大小是子View自身的LayoutParam的大小
            //测量模式是EXACTLY
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // 如果子View的大小是MATCH_PARENT
                //则 子View的大小是父View的除去padding和margin的大小
                //测量模式是EXACTLY
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                //如果子View的大小是WRAP_CONTENT
                //则子View的大小是子View的大小是父View的除去padding和margin的大小
               //测量模式是AT_MOST
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        //  // 如何父布局大小是精确的大小或者是WARP_CONTENT
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                //如果子View自身的LayoutParam的大小>0
            //则子View大大小是子View自身的LayoutParam的大小
            //测量模式是EXACTLY
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // 如果子View的大小是MATCH_PARENT
                //则 子View的大小是父View的除去padding和margin的大小
                //测量模式是AT_MOST
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                //如果子View的大小是WRAP_CONTENT
                //则子View的大小是子View的大小是父View的除去padding和margin的大小
               //测量模式是AT_MOST
                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);
    }

总结:对于普通的View,它的MeasureSpec由父视图的MeasureSpec和其自身的LayoutParams共同决定。UNSPECIFIED模式主要用于系统内部多次Measure的情形,一般不需关注。

七、View的measure流程

private void perormMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    ...
    // 具体的测量操作分发给ViewGroup
    mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
}

// 在ViewGroup中的measureChildren()方法中遍历测量ViewGroup中所有的View
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
    final int size = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < size; ++i) {
        final View child = children[i];
        // 当View的可见性处于GONE状态时,不对其进行测量
        if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }
    }
}

// 测量某个指定的View
protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
    final LayoutParams lp = child.getLayoutParams();

    // 根据父容器的MeasureSpec和子View的LayoutParams等信息计算
    // 子View的MeasureSpec
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height);
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

// View的measure方法
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    ...
    // ViewGroup没有定义测量的具体过程,因为ViewGroup是一个
    // 抽象类,其测量过程的onMeasure方法需要各个子类去实现
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    ...
}

// 不同的ViewGroup子类有不同的布局特性,这导致它们的测量细节各不相同,如果需要自定义测量过程,则子类可以重写这个方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // setMeasureDimension方法用于设置View的测量宽高
    setMeasureDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 
    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

// 如果View没有重写onMeasure方法,则会默认调用getDefaultSize来获得View的宽高
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 = sepcSize;
            break;
    }
    return result;
}

  • 对getSuggestMinimumWidth的分析
protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinmumWidth());
}

protected int getSuggestedMinimumHeight() {
    return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}

public int getMinimumWidth() {
    final int intrinsicWidth = getIntrinsicWidth();
    return intrinsicWidth > 0 ? intrinsicWidth : 0;
}

如果View没有设置背景,那么返回android:minWidth这个属性所指定的值,这个值可以为0;如果View设置了背景,则返回android:minWidth和背景的最小宽度这两者中的最大值。

  • 在Activity中获取某个View的宽高

    由于View的measure过程和Activity的生命周期方法不是同步执行的,如果View还没有测量完毕,那么获得的宽/高就是0。所以在onCreate、onStart、onResume中均无法正确得到某个View的宽高信息。解决方式如下:

    1、Activity/View#onWindowFocusChanged

    // 此时View已经初始化完毕
    // 当Activity的窗口得到焦点和失去焦点时均会被调用一次
    // 如果频繁地进行onResume和onPause,那么onWindowFocusChanged也会被频繁地调用
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            int width = view.getMeasureWidth();
            int height = view.getMeasuredHeight();
        }
    }
    

    2、view.post(runnable)

    // 通过post可以将一个runnable投递到消息队列的尾部,// 然后等待Looper调用此runnable的时候,View也已经初
    // 始化好了
    protected void onStart() {
    super.onStart();
    view.post(new Runnable() {
    
        @Override
        public void run() {
            int width = view.getMeasuredWidth();
            int height = view.getMeasuredHeight();
        }
    });
    }
    

    3、ViewTreeObserver

    // 当View树的状态发生改变或者View树内部的View的可见// 性发生改变时,onGlobalLayout方法将被回调
    protected void onStart() {
        super.onStart();
    
        ViewTreeObserver observer = view.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    
            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {
                view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                int width = view.getMeasuredWidth();
                int height = view.getMeasuredHeight();
            }
        });
    }
    

    4、View.measure(int widthMeasureSpec, int heightMeasureSpec)

八、View的Layou过程

/ ViewRootImpl.java
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) {
    ...
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    ...
}

// View.java
public void layout(int l, int t, int r, int b) {
    ...
    // 通过setFrame方法来设定View的四个顶点的位置,即View在父容器中的位置
    boolean changed = isLayoutModeOptical(mParent) ? 
    set OpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    ...
    onLayout(changed, l, t, r, b);
    ...
}

// 空方法,子类如果是ViewGroup类型,则重写这个方法,实现ViewGroup
// 中所有View控件布局流程
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

}

九、View的Draw过程

private void performDraw() {
    ...
    draw(fullRefrawNeeded);
    ...
}

private void draw(boolean fullRedrawNeeded) {
    ...
    if (!drawSoftware(surface, mAttachInfo, xOffest, yOffset, 
    scalingRequired, dirty)) {
        return;
    }
    ...
}

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, 
int xoff, int yoff, boolean scallingRequired, Rect dirty) {
    ...
    mView.draw(canvas);
    ...
}

// 绘制基本上可以分为六个步骤
public void draw(Canvas canvas) {
    ...
    // 步骤一:绘制View的背景
    drawBackground(canvas);

    ...
    // 步骤二:如果需要的话,保持canvas的图层,为fading做准备
    saveCount = canvas.getSaveCount();
    ...
    canvas.saveLayer(left, top, right, top + length, null, flags);

    ...
    // 步骤三:绘制View的内容
    onDraw(canvas);

    ...
    // 步骤四:绘制View的子View
    dispatchDraw(canvas);

    ...
    // 步骤五:如果需要的话,绘制View的fading边缘并恢复图层
    canvas.drawRect(left, top, right, top + length, p);
    ...
    canvas.restoreToCount(saveCount);

    ...
    // 步骤六:绘制View的装饰(例如滚动条等等)
    onDrawForeground(canvas)
}
  • setWillNotDraw的作用

    // 如果一个View不需要绘制任何内容,那么设置这个标记位为true以后,
    // 系统会进行相应的优化。
    public void setWillNotDraw(boolean willNotDraw) {
        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
    }
    
    • 认情况下,View没有启用这个优化标记位,但是ViewGroup会默认启用这个优化标记位。

    • 件继承于ViewGroup并且本身不具备绘制功能时,就可以开启这个标记位从而便于系统进行后续的优化。

    • 个ViewGroup需要通过onDraw来绘制内容时,我们需要显示地关闭WILL_NOT_DRAW这个标记位

[原文参考链接]https://jsonchao.github.io/2018/10/28/Android%20View%E7%9A%84%E7%BB%98%E5%88%B6%E6%B5%81%E7%A8%8B/)

相关文章

网友评论

      本文标题:View绘制流程解析

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