一、目录
二、简介
在理解View绘制流程,之前我们先解析下View绘制中的几大重要成员。
-
Window
- 介绍:Window表示一个窗口,用来承载View的抽象类。不管是Activity、Dialog还是Toast,它们的视图实际上都是附加在Window上的,因此Window实际是View的直接管理者。他的唯一实现是PhoneWindow
-
WindowManager
- 简介:WindowManager继承自ViewManager的接口。WindowManager主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。
-
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的构成。
四、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加载程序流执行程图
五、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();
}
流程图如下:
- 注: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/)