前言
一个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/gaugamela/article/details/53067769
相对而言ActivityThread的main方法是应用程序的入口,main()方法里做一些初始化工作,其中包括和AMS建立起通信。
代码语言:txt复制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类,用于发送生命周期改变的消息,通知响应操作。
代码语言:txt复制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()。
代码语言:txt复制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_35938012/article/details/81055380
而WindowManager实现类为WindowManagerImpl,WindowManagerImpl中addView()方法又会调用WindowManagerGlobal的addView()方法。参考如下:
代码语言:txt复制https://www.jianshu.com/p/c223b993b1ec
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()方法。
代码语言:txt复制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的绘制流程是从哪儿开始的,接下来分析这个过程到底是怎么做的。
代码语言:txt复制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测量的宽高和宽高的测量模式,大大节约开销。
代码语言:txt复制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亦是如此
代码语言:txt复制//生成DecorView根View的MeasureSpec
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
measure流程开始执行之前,会先计算出DecorView的MeasureSpec。此处mWidth和mHeight就为屏幕的宽高,LayoutParmas都为match_parent。
代码语言:txt复制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树的测量。
代码语言:txt复制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()方法。
代码语言:txt复制public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
onMeasure()方法意在二种:相对于ViewGroup来说
- 测量出子View的MeasureSpec后,再执行子View的measure流程
- 给自己mMeasureWidth&Height赋值。
View的onMeasure()方法就只干第二件事。我们以下xml布局为例,当我们调用setContentView(R.layout.activity_main)后:
代码语言:txt复制<?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()方法。
代码语言:txt复制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流程。
代码语言:txt复制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时有着固定的套路:
- 受父View的MeasureSpec影响
- 受子View自身的LayoutParams影响
- 计算父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总结如下:
- 子View写si宽高:测量模式不受父View影响,全都为EXACTLY,宽高为写si的宽高
- 子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最终的大小都不会超过这个范围,即使出现以下情况都是如此:
- 在720-1280屏幕下,给View设置了一张1500-1500的图片
- 子View的大小已经超过了自己
View最终的mMeasureWidth/Height,是由自身的测量模式,前/背景和子View的大小共同决定的。
代码语言:txt复制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);
}
代码语言:txt复制<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赋值就行了。
代码语言:txt复制protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
如果我们有给View设置background,getSuggestedMinimumWidth()会获取该大小,但是getDefaultSize()方法还是会选择父View帮助测量的MeasureSize。
代码语言:txt复制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的大小。
代码语言:txt复制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。
代码语言:txt复制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而言:
- 确定自己在父View中的位置
- 遍历所有子View,计算出在自己心中的位置(4个点)后,再执行子View的layout流程
相对于View(单个View)而言只干第一件事。
draw流程
performDraw()方法中会执行通过层层调用会执行到View的draw()方法。
代码语言:txt复制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类中已经实现:
代码语言:txt复制protected void dispatchDraw(Canvas canvas) {
more |= drawChild(canvas, child, drawingTime);
}
总结
View的绘制流程到此结束,不足支持多多包涵,指出共同探讨。
本文转自 【郭霖】 如有侵权,请联系删除。