setContentView
一个View,从无到有会走三个流程,也就是老生常谈的measure,layout,draw三流程;
在了解view绘制流程之前,先简单回顾下setContentView方法。
@Override
public void setContentView(@LayoutRes int layoutResID) {
//getDelegate是一个抽象类 AppCompatDelegateImpl继承了getDelegate
getDelegate().setContentView(layoutResID);
}
@Override
public void setContentView(int resId) {
ensureSubDecor();//创建DecorView
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);//将我们自己的xml布局填充到android.R.id.content布局上面去
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
首先初始化mDecor,即DecorView为FrameLayout的子类。就是我们整个窗口的根视图了。
然后,通过infalter.inflater放入到我们的mDecor中。
最后,我们在Activity中设置的布局,会通过infalter.inflater压入到我们的id为content的FrameLayout中去
handleResumeActivity
当Activity的生命周期发生改变,经过层层调用执行到handleResumeActivity()方法,在方法中先调用Activity.onResume()方法,再执行WindowManager的addView()方法将Activity的根View(DecorView)添加上去,进而开始绘制流程。
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
if (r.window ==null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow(); //phoneWindow
View decor = r.window.getDecorView();//phoneWindow.DecorView
decor.setVisibility(View.INVISIBLE);//decor设置INVISIBLE
ViewManager wm = a.getWindowManager();
a.mDecor = decor;
//WindowManagerImpl.addview 把我们的decor添加到WindowManager
wm.addView(decor, l);
......
}
}
WindowManagerImpl.addview将decorview传给了WindowManagerGlobal
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
//mGlobal是一个单例 WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
WindowManagerGlobal.addview
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
//开始流程绘制 view是DecorView
root.setView(view, wparams, panelParentView);
}
ViewRootImpl.setview
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
.......
requestLayout();
//将窗口添加到wms
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
.......
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();//检查是不是主线程
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//mTraversalRunnable是一个Runnable 内部执行了doTraversal performTraversals
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
private void performTraversals(){
......
//进行预测量
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
//计算DecorView根View的MeasureSpec
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, mWidth, mHeight);
performDraw();
}
//预测量
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
int childWidthMeasureSpec;
int childHeightMeasureSpec;
boolean windowSizeMayChange = false;
boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight()
+ ") from width spec: " + MeasureSpec.toString(childWidthMeasureSpec)
+ " and height spec: " + MeasureSpec.toString(childHeightMeasureSpec));
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desire dWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(mTag, "Good!");
goodMeasure = true;
}
}
}
}
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
return windowSizeMayChange;
}
首先在进行performMeasure之前先进行一个预测量,如果view的宽度是WRAP_CONTENT的话进入if语句里面,然后获取res里面的配置dp赋值给baseSize,然后通过getRootMeasureSpec获取宽高进行测量,如果等于0的话代表满意,否则的话代表不满意接着测量(也就是说默认不给view那么大的大小,先给你360dp试试看,合不合适),然后继续给出大一点空间你,继续测量,如果还是不合适的话就全部给你
MeasureSpec是一个32的位的int值 高2位(左边开始数)是测量模式,低30位(右边开始数)是值也就是具体的数值
使用该类用一个int值就能记录View测量的宽高和宽高的测量模式,大大节约开销
然后调用我们通常讲的三大流程performMeasure,performLayout,performDraw方法
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
......
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
......
private void performDraw() {
......
boolean canUseAsync = draw(fullRedrawNeeded);
......
}
private boolean 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, Rect surfaceInsets){
......
mView.draw(canvas);
......
}
}
view.measure进行测量
public final void measure(int widthMeasureSpec, int heightMeasureSpec){
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
DecorView的measure()方法开始整个View树的测量,
measure()方法是被final修饰了的,子类都不能重写,所有View都会执行到View类的measure()方法。
onMeasure()方法意在二种:相对于ViewGroup来说
1.测量出子View的MeasureSpec后,再执行子View的measure流程
2.给自己mMeasureWidth&Height赋值。
我们以下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));
}
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);
}
layout流程
相对于measure流程而言,layout和draw流程就简单得多了,通过Layout流程来确定子View在父View中的位置。子View在父View中的位置,需要4个点来确定,同时也可以通过点的距离来计算出View的大小。
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。
draw
performDraw()方法中会执行通过层层调用会执行到View的draw()方法。
需要注意的是viewGroup不一定执行onDraw方法
同理也是执行DecorView.draw方法。DecorView.draw执行父类的View.draw()方法
protected void dispatchDraw(Canvas canvas) {
more |= drawChild(canvas, child, drawingTime);
}
performMeasure->View.measure->View.onMeasure->setMeasuredDimension:View不需要管子view直接设置宽高就行了
performLayout->host.Layout->View.onLayout:这是一个空方法(因为它不是一个容器不需要做设置left之类的参数,所以是一个空方法,通常是父容器遍历子view然后调用onLayout)
performDraw->Draw->drawSoftware->mView.draw(canvas):mView指的是DecorView