关于程序员:Android高级进阶之路一Android中View绘制流程浅析

44次阅读

共计 17668 个字符,预计需要花费 45 分钟才能阅读完成。

前言

一个 View,从无到有会走三个流程,也就是陈词滥调的 measure,layout,draw 三流程;

咱们都晓得 Android 视图是由一层一层形成的层级构造,直白点说,就是父 View 蕴含子 View 而子 View 又能够蕴含子 View。所以绘制流程是由最外层的 View 开始,一步一步向内传递执行。而整个过程又是递归期待的,最外层的 View 须要等内层所有的 View 执行完绘制流程才完结,所以便有了”缩小布局层级,能够无效晋升 App 性能”这一经典总结。

注释

什么时候开始绘制?

而万物有始才有终,你不惹他,他也不会入手打你。View 的绘制流程是什么时候开始的?谁触发的? 明确这点后,才去思考这个过程是怎么执行的。

咱们都分明 Activity 中 onCreate()办法在 setContentView()后,View 的宽高是获取不到的。同时咱们晓得 Activity 在 onResume()后才齐全可见,并且首次在 onResume()办法中也是拿不到 View 的尺寸的,这样能够推算得出:View 的绘制流程是在 onResume()办法执行完结后才开始的。那 Activity 的生命周期办法背地是由谁,又何时调用的?

答:ActivityManagerService 

ActivityManagerService(以下简称 AMS))是 Androids 下层零碎中最外围的服务之一,次要负责零碎中四大组件的启动、切换、调度及应用程序的治理和调度等工作。具体具体内容参考以下地址:

https://blog.csdn.net/gaugame…

相对而言 ActivityThread 的 main 办法是应用程序的入口,main()办法里做一些初始化工作,其中包含和 AMS 建设起通信。

public class ActivityThread{final ApplicationThread mAppThread = new ApplicationThread();
    final Looper mLooper = Looper.myLooper();
    final H mH = new H();
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();


    public static void main(String[] args) {
        // 初始化 lopper
        Looper.prepareMainLooper();
        // 初始化 ActivityThread
        ActivityThread thread = new ActivityThread();
        //ApplicationThread 和 AMS 建立联系
        thread.attach(false);
        // 取音讯
        Looper.loop();
        //loop()办法如果执行完结,未能取到音讯,程序抛出异样退出。throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}

ActivityThread 会治理和用户打交道的 Activity, 利用所有的 Activity 都存在 ActivityThread 中的 mActivities 汇合中,而 ActivityThread 响应 AMS 的号召,须要借助 ApplicationThread 来承受这个诏令,点进去看全都是生命周期办法。接着调用 attach()办法让 ApplicationThread 和 AMS 建立联系。H 类就是一个 Handler 类,用于发送生命周期扭转的音讯, 告诉响应操作。

private class ApplicationThread extends IApplicationThread.Stub {

    // 告诉相应的过程执行启动 Activity 的操作
    public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
            ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
            CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
            int procState, Bundle state, PersistableBundle persistentState,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {sendMessage(H.LAUNCH_ACTIVITY, r);
    }
    public final void scheduleResumeActivity(IBinder token, int processState,
            boolean isForward, Bundle resumeArgs) {sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0, 0, seq);
    }
    //.....

}

对于 AMS 我也不太懂在这儿提一下明确是怎么回事就行,当前再缓缓钻研。当 Activity 启动时会先调用到 scheduleLaunchActivity()办法,由 Handler 发送告诉音讯后执行 handleLaunchActivity()->performLaunchActivity()->callActivityOnCreate()->Activity.onCreate()。

private class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {
            case LAUNCH_ACTIVITY: {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
                // 该办法中会执行 Activity 的 onCreate()办法。handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
            //onResume();
             case RESUME_ACTIVITY:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
                SomeArgs args = (SomeArgs) msg.obj;
                handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
                        args.argi3, "RESUME_ACTIVITY");
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
        }
    }
}

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {ActivityClientRecord r = mActivities.get(token);

    //.............

    // 执行 onResume()办法
    r = performResumeActivity(token, clearHide, reason);

     if (r != null) {
        final Activity a = r.activity;
         if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;

            if (a.mVisibleFromClient) {if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    // 将 DecorView 增加到 Window 上
                    wm.addView(decor, l);
            } 
        }
    // 说法二: 执行 makeVisible()来增加 View,但也是增加到 Window 里和下面一样的操作。分明的小伙伴能够通知我下。if (r.activity.mVisibleFromClient) {r.activity.makeVisible();
        }
    }
}

onResume()时也一样,当 Activity 的状态产生扭转,通过层层调用执行到 handleResumeActivity()办法,在办法中先调用 Activity.onResume()办法,再执行 WindowManager 的 addView()办法将 Activity 的根 View(DecorView)增加下来,进而开始绘制流程。这就解释了为什么首次在 onResume()办法中获取不到 View 的宽高。对 DecorView 不太明确的能够参考 Activity 中 setContentView 浅析。地址如下所示:

https://blog.csdn.net/sinat_3…

而 WindowManager 实现类为 WindowManagerImpl,WindowManagerImpl 中 addView()办法又会调用 WindowManagerGlobal 的 addView()办法。参考如下:

https://www.jianshu.com/p/c22…

 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
    ······
    root = new ViewRootImpl(view.getContext(), display);

    view.setLayoutParams(wparams);

    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    // 行将开始流程绘制    
    root.setView(view, wparams, panelParentView);
    ·······
}

addView()办法中先创立 ViewRootImpl 对象, 随后执行 setView()办法将其和 DecorView 绑定起来,绘制流程也将由 ViewRootImpl()来执行。setView()办法中会执行 requestLayout()办法。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {if (mView == null) {
        mView = view;
         // Schedule the first layout -before- adding to the window
         // manager, to make sure we do the relayout before receiving
         // any other events from the system.
         requestLayout();}
}

requestLayout()办法走上来会异步执行 performTraversals()办法,View 的三大流程都是在该办法中执行的。到这儿咱们算是明确 View 的绘制流程是从哪儿开始的,接下来剖析这个过程到底是怎么做的。

private void performTraversals() {

    // 计算 DecorView 根 View 的 MeasureSpec
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

    performLayout(lp, mWidth, mHeight);

    performDraw();}

measure 流程

说到 measure 流程就不得提到一个类,MeausreSpec。应用该类用一个 int 值就能记录 View 测量的宽高和宽高的测量模式,大大节约开销。

public static class MeasureSpec {
    private static final int MODE_SHIFT = 30;

    //int 类型占 4 个字节,1 个字节 =8bit(位)。private static final int MODE_MASK  = 0x3 << MODE_SHIFT; //11000000000000000000000000000000

    public static final int UNSPECIFIED = 0 << MODE_SHIFT; //00000000000000000000000000000000  据说用于零碎外部,想要多大就给多大。平时也没有用到过,上面不做剖析。public static final int EXACTLY     = 1 << MODE_SHIFT; //01000000000000000000000000000000  准确值模式,对应 LayoutParams 的 match_parent 或者固定尺寸
    public static final int AT_MOST     = 2 << MODE_SHIFT; //10000000000000000000000000000000  最大值模式,对应 LayoutParams 的 wrap_content

     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);
        }
    }
    public static int getMode(int measureSpec) {return (measureSpec & MODE_MASK);
    }
    public static int getSize(int measureSpec) {return (measureSpec & ~MODE_MASK);
    }

}

用一句话解释 MeasureSepc:

用位运算的形式来”压缩”记录 View 的测量宽高和测量模式,其中高 (前) 两位代表测量模式后三十位代表测量后的尺寸。同时提供”解压”的办法转为咱们须要的理论数值。

MeasureSpec = MeasureMode+MeasureSize

咱们以 int mMeausreWidth = makeMeasureSepc(720,MeasureSpec.EXACTLY)为例: 

getMode 亦是如此

// 生成 DecorView 根 View 的 MeasureSpec
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

measure 流程开始执行之前,会先计算出 DecorView 的 MeasureSpec。此处 mWidth 和 mHeight 就为屏幕的宽高,LayoutParmas 都为 match_parent。

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 后,执行 DecorView 的 measure()办法开始整个 View 树的测量。

private void performMeasure()(int childWidthMeasureSpec, int childHeightMeasureSpec) {if (mView == null) {return;}
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

measure()办法是被 final 润饰了的,派生类都不能重写,所有 View 都会执行到 View 类的 measure()办法。

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {onMeasure(widthMeasureSpec, heightMeasureSpec);
}

onMeasure()办法意在二种: 绝对于 ViewGroup 来说 

  1. 测量出子 View 的 MeasureSpec 后,再执行子 View 的 measure 流程 
  2. 给本人 mMeasureWidth&Height 赋值。

View 的 onMeasure()办法就只干第二件事。咱们以下 xml 布局为例,当咱们调用 setContentView(R.layout.activity_main)后:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context=".MainActivity">
    <TextView
        android:layout_height="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World"/>
</LinearLayout>

此时此处 DecorView 有实现 onMeausre 办法并且会执行父类 FrameLayout 的 onMeausre()办法。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {for (int i = 0; i < count; i++) {final View child = getChildAt(i);
        if (mMeasureAllChildren || child.getVisibility() != GONE) {
            //core
            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);
                }
            }
        }
    }

    // 设置的前景
    maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
    maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();


    maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

    // 设置的 background
    final Drawable drawable = getForeground();
    if (drawable != null) {maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
    maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
    }
    // 给本人的 mMeasuredWidth 和 mMeasuredHeight 赋值
    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),resolveSizeAndState(maxHeight, heightMeasureSpec,childState <<MEASURED_HEIGHT_STATE_SHIFT));
}SizeAndState(maxHeight, heightMeasureSpec,childState <<MEASURED_HEIGHT_STATE_SHIFT));}

onMeasure()办法中遍历所有子 View,通过执行 measureChildWithMargins()办法,先计算出子 View 的 MeasureSpec 再调用子 View 的 measure()办法传递执行 measure 流程。

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);
    // 开始 LinearLayout 的 measure 流程
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}a

父 View 在帮忙计算子 View 的 MeasureSpec 时有着固定的套路: 

  1. 受父 View 的 MeasureSpec 影响 
  2. 受子 View 本身的 LayoutParams 影响 
  3. 计算父 View 剩下可用的区域,减去父 View 的 padding 和子 View 的 margin 间隔和父 View 曾经应用 (预约) 的区域大小。
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {

    // 父 View 的宽 / 高测量模式
    int specMode = MeasureSpec.getMode(spec);
    // 父 View 的宽 / 高大小
    int specSize = MeasureSpec.getSize(spec);

    // 父 View 剩下的可用区域
    int size = Math.max(0, specSize - padding);

    int resultSize = 0;
    int resultMode = 0;

    switch (specMode) {
    // 父 View_EXACTLY
    case MeasureSpec.EXACTLY:
        // 如果子 View 写 si 了宽 / 高
        if (childDimension >= 0) {// 子 View 的 MeasureSpec=EXACTLY+ 写 si 的宽 / 高(si 说多了不吉利)
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // 子 View 的 MeasureSpec=EXACTLY+ 父 View 剩下的区域
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {

            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    // 父 View_AT_MOST
    case MeasureSpec.AT_MOST:
        // 如果子 View 写死了宽高
        if (childDimension >= 0) {
            // 子 View 的 MeasureSpec=EXACTLY+ 写 si 的宽 / 高
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // 子 View 的 MeasureSpec=AT_MOST+ 父 View 剩下的区域
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;

    // 父 View_UNSPECIFIED 素来没有用到,不做剖析
    case MeasureSpec.UNSPECIFIED:
        if (childDimension >= 0) {

            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {

            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {

            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }

    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

getChildMeasureSpec()生产子 View 的 MeasureSpec 总结如下: 

  1. 子 View 写 si 宽高:测量模式不受父 View 影响,全都为 EXACTLY,宽高为写 si 的宽高 
  2. 子 View 没有写 si 宽高: 如果父 View 都为 AT_MOST,子 View 想都别想还是为 AT_MOST,如果父 View 为 EXACTLY 且子 View 的 LayoutParams 为 match_parent, 才为 EXACTLY。宽高都为父 View 剩下的区域。这就很好的明确了为什么咱们自定义 View 时,如果没对 View 的宽高进行解决,View 即便是 wrap_content 也会撑满整个屏幕了。

如果咱们写 si 的尺寸超过了无效范畴,比方超出了屏幕或者超过了父 View 的大小,最终的 measureWidth/Height 和理论宽高还是写死的尺寸,只不过超出的区域看不见而已。

ViewGroup 在所有子 View 的 measure 流程都执行完结后,再调用 setMeasuredDimension()办法给本人的 mMeasureWidth/Height 赋值。其实 View 在执行 onMeausre()办法之前,曾经由父 View(DecorView 除外)计算出了一个无效的 MeasureSpec,比方在执行 performMeasure()办法之前就先一步计算出了 DecorView 的 MeasureSpec,接着在 measureChildWithMargins()办法中又先计算出 LinearLayout 的 MeasureSpec,再执行 LinearLayout 的 measure()流程。并且 View 最终的大小都不会超过这个范畴,即便呈现以下状况都是如此:

  1. 在 720-1280 屏幕下,给 View 设置了一张 1500-1500 的图片 
  2. 子 View 的大小曾经超过了本人 

View 最终的 mMeasureWidth/Height,是由本身的测量模式,前 / 背景和子 View 的大小独特决定的。

setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),resolveSizeAndState(maxHeight, heightMeasureSpec,childState <<MEASURED_HEIGHT_STATE_SHIFT));

public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {final int specMode = MeasureSpec.getMode(measureSpec);

    final int specSize = MeasureSpec.getSize(measureSpec);
    switch (specMode) {
        case MeasureSpec.AT_MOST:
            if (specSize < size) {result = specSize | MEASURED_STATE_TOO_SMALL;} else {result = size;}
            break;
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        case MeasureSpec.UNSPECIFIED:
        default:
            result = size;
    }
    return result | (childMeasuredState & MEASURED_STATE_MASK);
}
<com.example.yangjie.application.MyLinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/ll_test"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:context=".MainActivity">
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    Log.i("TAG","LinearLayoutWidth="+MeasureSpec.getSize(widthMeasureSpec));
    Log.i("TAG","LinearLayoutHeight="+MeasureSpec.getSize(heightMeasureSpec));
    Log.i("TAG","MeasureWidth="+getMeasuredWidth());
}

当 LinearLayout 的 LayoutParams 时 match_parent 时好说,LinearLayout 的 MeasureMode 为 EXACTLY,size 就是父 View 帮其计算出的 MeasureSize。如果 LinearLayout 的 LayoutParams 为 warp_content,在执行 resolveSizeAndState()办法时会走到 case MeasureSpec.AT_MOST: 外面去。View 最终的宽高会从本身的前 / 背景大小和子 View 的大小当选则一个最大值。在 FrameLayout 中会选出最大的子 View 的 measureWidth/Height,因为 FrameLayout 的子 View 都是重叠放在左上角的,所以选出最大的那一个就行了。而 LinearLayout 会累计所有子 View 的大小。当然如果这个最大值超过了父 View 为其测量的 MeasureSize,最终 View 的大小还是为父 View 为其测量的 MeasureSize。specSize | MEASURED_STATE_TOO_SMALL; 仅仅只是为了标记一个这个 View 的测量状态,在 getMeasureWidth/Height()时值还是不变的。

ViewGroup 的 onMeausre()办法明确之后,再看 View 的就简略多了,给 View 的 mMeasureWidth 和 Height 赋值就行了。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

如果咱们有给 View 设置 background,getSuggestedMinimumWidth()会获取该大小,然而 getDefaultSize()办法还是会抉择父 View 帮忙测量的 MeasureSize。

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

layout 流程

绝对于 measure 流程而言,layout 和 draw 流程就简略得多了,通过 Layout 流程来确定子 View 在父 View 中的地位。子 View 在父 View 中的地位,须要 4 个点来确定,同时也能够通过点的间隔来计算出 View 的大小。

public final int getWidth() {return mRight - mLeft;}

public final int getHeight() {return mBottom - mTop;}

performLayout 办法中会执行 DecorView 的 layout()办法来开始整个 View 树的 layout 流程。而 DecorView 包含其余的 ViewGroup 都没有另外实现 layout()办法,都会执行到 View 的 layout()办法。layout()办法中会先执行 setFrme()办法确定 View 本人在父 View 中的地位,接着再执行 onLayout()办法来遍历所有的子 View,计算出子 View 在本人心中的地位 (4 个点) 后,再执行子 View 的 layout 流程。不同的 ViewGroup 有着不同的形式来安顿子 View 在本人心中的地位。所以 View 类中的 onLayout()是一个空办法,等着 View 们本人去实现。自定义 ViewGroup 的时候如果不在 onLayout 办法中安顿子 View 的地位,将看不见子 View。

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
    mLayoutRequested = false;
    mScrollMayChange = true;
    mInLayout = true;

    final View host = mView;

    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}

public void layout(int l, int t, int r, int b) {if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    }

    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;

    boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {onLayout(changed, l, t, r, b);

    }
}

laout 流程,绝对于 ViewGroup 而言: 

  1. 确定本人在父 View 中的地位 
  2. 遍历所有子 View,计算出在本人心中的地位 (4 个点) 后,再执行子 View 的 layout 流程 

绝对于 View(单个 View)而言只干第一件事。

draw 流程

performDraw()办法中会执行通过层层调用会执行到 View 的 draw()办法。

private void performDraw() {draw(fullRedrawNeeded);
}
private void draw(boolean fullRedrawNeeded) {if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {return;}
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty) {mView.draw(canvas);
}
public void draw(Canvas canvas) {
    // 绘制本人的背景
    drawBackground(canvas);
    // 空实现,绘制本人的内容,自定义时重写该办法
    onDraw(canvas)
    // 绘制子 View
    dispatchDraw(canvas);
    // 绘制前景
    onDrawForeground(canvas);

}

draw()办法会绘制一些本人的货色。通过 dispatchDraw()办法来传递执行子 View 的 draw 流程。ViewGroup 类中曾经实现:

protected void dispatchDraw(Canvas canvas) {more |= drawChild(canvas, child, drawingTime);
}

总结

View 的绘制流程到此结束,有余反对多多包涵,指出独特探讨。

本文转自【郭霖】如有侵权,请分割删除。

正文完
 0