来看看这个Activity这个类
跟进来setContentView方法,里面调用了getWindow这个方法获取了PhoneWindow对象,在调用了setContentView这个方法;
PhoneWindow对象的创建是在attach这个方法中,attach方法是在Activity创建之后调用的,在各种生命周期之前;
initWindowDecorActionBar方法的解释,请往下看↓
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
......
private Window mWindow;
......
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
//PhoneWindow对象的创建
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
......
......
;
}
......
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
//看名字大概知道是初始化窗口的导航栏
initWindowDecorActionBar();
}
......
public Window getWindow() {
return mWindow;
}
......
}
来看看这个PhoneWindow类中的setContentView方法的调用
mContentParent为空时,调用installDecor方法;
installDecor方法中
1、创建DecorView;调用generateDecor方法,在这个方法中,获取上下文,然后直接new了一个DecorView对象,这个对象是一个FrameLayout容器,FrameLayout是ViewGroup的子类,ViewGroup是View的子类;
2、创建mContentParent ,调用generateLayout方法,这个方法大家自己可以去看看;
generateLayout这方法中会获取对应的特性;根据特性去创建布局,也就是我们Activity布局的外层布局(FrameLayout),包裹着Activity布局的那一层,我们Activity布局的外层是一个FrameLayout;
mContentParent 也就是这个外层的FrameLayout
DecorView 和mContentParent 这两个容器创建完成后,通过布局加载器,将我们传入进来的布局id,加载成视图树,然后添加到mContentParent 容器当中;
ViewGroup mContentParent;
private DecorView mDecor;
@Override
public void setContentView(int layoutResID) {
//mContentParent 是一个ViewGroup容器
if (mContentParent == null) {
//创建DecorView 和mContentParent 容器
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//布局加载器加载我们传入进来的布局id,加载好视图树,然后添加到mContentParent里面去;
//mContentParent是一个FrameLayout布局
mLayoutInflater.inflate(layoutResID, mContentParent);
}
......
}
//创建DecorView 和mContentParent 容器
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//创建mContentParent 容器 这方法中会获取对应的特性;
//根据特性去创建布局,也就是我们Activity布局的外层布局(FrameLayout),包裹着Activity布局的那一层,
//我们Activity布局的外层是一个FrameLayout;
//mContentParent 也就是这个外层的FrameLayout
mContentParent = generateLayout(mDecor);
}
}
//创建DecorView 容器
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext) {
//获取到Application的上下文
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext());
if (mTheme != -1) {
//设置主题
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
//new 了一个DecorView
return new DecorView(context, featureId, this, getAttributes());
}
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
......
}
Activity中的initWindowDecorActionBar方法
这个方法是调用了PhoneWindow的setContentView这个方法后调用的,来看看这个方法里面;
initWindowDecorActionBar方法中:
1、获取PhoneWindow对象;
2、通过PhoneWindow对象获取了一下DecorView容器;
3、创建导航栏
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
private void initWindowDecorActionBar() {
//获取了PhoneWindow对象
Window window = getWindow();
//通过PhoneWindow对象获取DecorView容器
window.getDecorView();
if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}
//创建导航栏
mActionBar = new WindowDecorActionBar(this);
mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
mWindow.setDefaultIcon(mActivityInfo.getIconResource());
mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
}
}
1、继承Activity这个父类时;调用setContentView方法的,会去拿到PhoneWindow对象,调用这个PhoneWindow对象的setContentView方法,在这个setContentView方法中会去创建DecorView(FrameLayout的子类) 和mContentParent 这两个容器,mContentParent 容器是一个FrameLayout布局容器,通过findViewById这个方法传入ID_ANDROID_CONTENT这个常量获取到,然后通过布局加载器,将我们传入的布局id,获取到布局文件,加载成视图树,添加到DecorView这个FrameLayout当中,并且创建ViewRootImpl对象,与这个DecorView关联起来 ;
2、PhoneWindow对象的创建是在attach这个方法中,attach方法是在Activity创建之后调用的,在各种生命周期之前;
3、onCreate方法里面只是做解析xml保存成视图树,视图的测量、布局摆放、绘制是在获取到焦点后这个onResume生命周期后;
下图
PhoneWindow、DecorView、mContentRoot、mContentParent的关系
(借用网络上的图片)
View:所有可视化控件的父类;
ViewGroup:View类的子类,可以拥有子控件,可以看作是容器
View绘制流程:测量–》布局摆放–》绘制
测量是从DecorView里面开始测量的;
ViewRootImpl是是View中的最高层级,属于所有View的根;WindowManager和DecorView的之间的通讯,View的绘制三大流程都是通过ViewRootImpl类里面来完成的。Activity创建后,会把DecorView添加到WindowManager中,同时会创建ViewRootImpl对象,使DecorView和ViewRootImpl建立联系;在DecorView中增删改子View都会用到ViewRootImpl;
有兴趣的童鞋具体可以去看看WindowManagerGlobal这个类里面的addView这个方法中的代码;
ViewRootImpl中的任务:
我们来看看ViewRootImpl中这个performTraversals方法的调用
WindowManagerGlobal中的addView添加视图的方法中,创建了ViewRootImpl对象,然后调用了ViewRootImpl的setView方法,在这个setView方法中调用到requestLayout方法
从requestLayout开始看,requestLayout中:
1、主线程检测;
2、调用scheduleTraversals方法,这个方法中通过Handler去执行Runnable,这个Runnable最终执行到performTraversals这个遍历视图树的方法;
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//线程检测,这里就是为什么在子线程中刷新ui会异常
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//这方法中是通过handler去执行这个mTraversalRunnable,里面的代码大家可以自己去看看
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
//主线程中调用doTraversal这个方法
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
......
performTraversals();
.....
}
}
......
private void performTraversals() {
//把DecorView赋值到局部变量中
final View host = mView;
......
if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration) {
//获取到子试图测量的规格
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
+ " coveredInsetsChanged=" + contentInsetsChanged);
// 执行View的测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
......
//执行view的布局摆放
performLayout(lp, mWidth, mHeight);
......
......
//执行绘制
performDraw();
} else {
......
}
......
}
//用于测量根视图的测量规范
//根据布局的尺寸方式,进行获取测量的规则;
//MATCH_PARENT、WRAP_CONTENT 这两个想必大家应该不会陌生,在写布局时候常用到的
//第一个参数是父容器的size
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT: //-1
// Window can't resize. Force root view to be windowSize.
//窗口无法调整大小,强制根视图为windowSize
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT: //-2
// 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;
}
//执行View的测量
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//mView是DecorView,而DecorView是View的子类,DecorView里面没有重写measure方法,下面跟进去看View这个measure方法的调用
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
View里面的measure方法调用
measure方法中的主线调用了onMeasure方法,传入进去宽和高的尺寸规格;这里也就是个保存宽和高;
int mMeasuredWidth;
int mMeasuredHeight;
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
.......
if (forceLayout || needsLayout) {
.......
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// 调用onMeasure方法,传入进去宽和高的尺寸规格
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
......
}
......
}
......
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//保存当前View的宽和高
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
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;
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
//将宽和高保存到了全局变量中mMeasuredWidth 、mMeasuredHeight
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
MeasureSpec中的三个模式
UNSPECIFIED 模式: 父View不对子View有任何限制,子View需要多大就多大;(未指定尺寸模式)-------一般在ScrollView,ListView
EXACTYLY 模式: 父View已经测量出子Viwe所需要的精确大小,这时候View的最终大小; 就是SpecSize所指定的值。对应于match_parent和精确数值这两种模式;(精准尺寸模式)------match_parent,精确值是父View宽高;
AT_MOST 模式: 子View的最终大小是父View指定的SpecSize值,并且子View的大小不能大于这个值,即对应wrap_content这种模式;(最大尺寸模式)------wrap_content 精确值不超过父View宽高
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
......
......
/**
* UNSPECIFIED 模式:
* 父View不对子View有任何限制,子View需要多大就多大
*/
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
/**
* EXACTYLY 模式:
* 父View已经测量出子Viwe所需要的精确大小,这时候View的最终大小
* 就是SpecSize所指定的值。对应于match_parent和精确数值这两种模式
*/
public static final int EXACTLY = 1 << MODE_SHIFT;
/**
* AT_MOST 模式:
* 子View的最终大小是父View指定的SpecSize值,并且子View的大小不能大于这个值,
* 即对应wrap_content这种模式
*/
public static final int AT_MOST = 2 << MODE_SHIFT;
......
//将size和mode打包成一个32位的int型数值
//高2位表示SpecMode,测量模式,低30位表示SpecSize,某种测量模式下的规格大小
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);
}
}
//将32位的MeasureSpec解包,返回SpecMode,测量模式
public static int getMode(int measureSpec) {
//noinspection ResourceType
return (measureSpec & MODE_MASK);
}
//将32位的MeasureSpec解包,返回SpecSize,某种测量模式下的规格大小
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
}
DecorView是一个FrameLayout,我们去看看FrameLayout里面的测量
1、获取当前布局容器FrameLayout中的子视图个数;
2、遍历子视图,传入自己的宽高规格,得到子视图的宽度和高度,子视图也有可能是个容器,所以子视图中会存在再次遍历自己容器中的子视图;
3、如果子视图是个容器的话,等到子视图容器都遍历完成之后,获取到这个这个子视图容器的宽高;
4、通过拼接子视图设置的边距,计算出最大的宽高值;
public class FrameLayout extends ViewGroup {
......
......
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获取当前布局内的子视图的个数
int count = getChildCount();
//判断当前布局FrameLayout的宽高是否是MATCH_PARENT模式或者指定一个精确的大小,如果是则measureMatchParentChildren设置为false;
//当前FrameLayout为wrap_content时为true
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
//遍历所有类型不为GONE(不为隐藏)的子View
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//对每一个子View进行测量,传入子View和父容器的宽高规格
// 测量单个View,需要考虑当前ViewGroup的MeasureSpec和Padding、margins
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//子视图里面的视图都遍历完成后,得到子视图的最大宽度;
//child.getMeasuredWidth()就是子视图里面的视图都测量完成之后,得到这个子视图宽度
//通过得到这个子视图的宽度加上子视图设定的左右外边距后,得到最大的宽度
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());
//如果父容器设定的是自适应wrap_content的话,就需要把子视图都保存进来
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
// Account for padding too
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// 得出这个视图树中最大的宽高数
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Check against our foreground's minimum height and width
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
//保存最大宽高和宽高的规格;
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
//这里是在父容器设定了自适应的情况下,再次去计算出子视图的宽高规格;
//因为是FrameLayout,所以得计算出自己需要的宽高,但是这个宽高是取决于子视图最大一个宽高的,
//所以这里得重新在计算出宽高规格,才能计算出自己需要的宽高;
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0, getMeasuredWidth()
- getPaddingLeftWithForeground() - getPaddingRightWithForeground()
- lp.leftMargin - lp.rightMargin);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
final int height = Math.max(0, getMeasuredHeight()
- getPaddingTopWithForeground() - getPaddingBottomWithForeground()
- lp.topMargin - lp.bottomMargin);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
height, MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
//重新在测量一下子视图
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
public int getChildCount() {
return mChildrenCount;
}
}
ViewGroup里面的measureChildWithMargins方法
因为上面FrameLayout的父类是ViewGroup,而且measureChildWithMargins放在在FrameLayout中没有实现,所以调用到父类ViewGroup中;
方法中通过计算父控件的内边距、子控件的外边距、父宽高规格,得到子控件最大的宽高度规格;
最后一行代码,的调用取决于布局中这个子控件是一个容器还是View视图,如果是个容器的话,就会调用该容器的measure方法;比如当前这个FrameLayout中是一个LinearLayout,那么就会调用LinearLayout的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);
//子视图的measure方法调用,传入宽高的规格
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
LinearLayout容器中的onMeasure方法
不管是横向还是垂直的方式测量,里面都会去遍历子视图,然后计算出子视图的宽高规格,最终也会调用child.measure(宽,高);
然后就和上面一样,如果还是容器的话还是会遍历自己里面的子视图;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//子布局的排列方向
if (mOrientation == VERTICAL) {
//垂直
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
//横向
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
打个比方…比方是谁?我们为什么要打他?。。。。
下面是我们自己的视图;
看过上面setContentView内容的应该就会知道,我们布局的上一层是一个FrameLayout布局容器;这个FrameLayout遍历子视图;
当前是只有一个子视图(最外层LinearLayout),那就是最外层这个LinearLayout,计算出这个子视图(最外层LinearLayout)的宽高规格后,调用子视图(最外层LinearLayout)的measure方法,也就是调用LinearLayout的measure方法;进入到LinearLayout容器中的onMeasure方法;
这个LinearLayout是个垂直布局,然后去遍历自己得子视图,第二层还是个LinearLayout(第二层LinearLayout),这个LinearLayout里面还是有子视图,接着自己还会去遍历里面的子视图(两个TextView)然后遍历完成后,得到子视图TextView的宽度,往上传到过去最大宽度,然后上层拿到这个最大宽度,在最外层FrameLayout中,获取到子视图的内容宽度和高度之后,然后分别加子视图设定的上左右和上下边距之后得到子视图(最外层LinearLayout)的最大宽度和高度;
如果说外层FrameLayout的宽或者高是用的自适应,那么再次去重新计算,根据得到最大的那个子视图的最大值计算出自己需要的宽和高的规格
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:onClick="click"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:onClick="click"
/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:onClick="click"
/>
</LinearLayout>
下面我们来看看布局摆放
同样还是ViewRootImpl 类中performTraversals这个方法中,经过测量后会调用performLayout这个方法进行布局摆放
在performTraversals这个方法中,测量完成后调用performLayout(lp, mWidth, mHeight);调用,下面来看看这个performLayout方法中的代码
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
//host赋值的是DecorView这个FrameLayout容器
final View host = mView;
if (host == null) {
return;
}
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
Log.v(mTag, "Laying out " + host + " to (" +
host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
//左 上 右 下
//这参数的布局含义是 从左上角开始;
//调用到
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
// requestLayout() was called during layout.
// If no layout-request flags are set on the requesting views, there is no problem.
// If some requests are still pending, then we need to clear those flags and do
// a full request/measure/layout pass to handle this situation.
ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
false);
if (validLayoutRequesters != null) {
// Set this flag to indicate that any further requests are happening during
// the second pass, which may result in posting those requests to the next
// frame instead
mHandlingLayoutInLayoutRequest = true;
// Process fresh layout requests, then measure and layout
int numValidRequests = validLayoutRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = validLayoutRequesters.get(i);
Log.w("View", "requestLayout() improperly called by " + view +
" during layout: running second layout pass");
view.requestLayout();
}
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mHandlingLayoutInLayoutRequest = false;
// Check the valid requests again, this time without checking/clearing the
// layout flags, since requests happening during the second pass get noop'd
validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
if (validLayoutRequesters != null) {
final ArrayList<View> finalRequesters = validLayoutRequesters;
// Post second-pass requests to the next frame
getRunQueue().post(new Runnable() {
@Override
public void run() {
int numValidRequests = finalRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = finalRequesters.get(i);
Log.w("View", "requestLayout() improperly called by " + view +
" during second layout pass: posting in next frame");
view.requestLayout();
}
}
});
}
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
跟进去host.layout(左,上,右,下)这个方法,进入View这个类
跟进View类中的layout方法,这个方法中调用到onLayout方法;
但是发现onLayout方法在这个View类中没有实现里面的代码逻辑;
看上面的performLayout方法中调用时,host是DecorView调用的,而DecorView是一个FrameLayout,所以到FrameLayout中的onLayout方法中去查看实现;
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方法
onLayout(changed, l, t, r, b);
.......
}
.......
}
//这个onLayout方法在view中没有实现逻辑
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
FrameLayout中的onLayout方法
我们来看看这个FrameLayout的onLayout方法里面的代码
和测量一样,大致也是遍历子视图,然后计算子视图的上面和左边的位置之后,调用子视图的layout方法,将子视图的上下左右的位置传递进去,子视图去做内容的布局摆放;如果说子视图也是个容器,那么里面还会对子视图的遍历以及调用子视图的layout方法;
代码流程:
1、获取子视图的个数;
2、获取当前容器的上下、左右内边距;
3、遍历所有的子视图,获取子视图的布局参数、子视图的宽高;
4、根据横向对齐方式,来计算出左边的起点位置;
5、根据垂直对齐方式,来计算出上面的起点位置;
6、然后调用子控件的layout方法,将子视图的上下左右的位置传递给子视图;
如果说子视图也是个容器,那么里面还会对子视图的遍历以及调用子视图的layout方法
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//布局子view
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
//得到子视图的个数
final int count = getChildCount();
//获取父容器左右内边距
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
//获取父容器上下内边距
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
//遍历子视图
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
//这里过滤掉隐藏的子控件 GONE状态的
if (child.getVisibility() != GONE) {
//获取布局参数
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//获取子控件的宽高
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
//子视图的左边起点位置
int childLeft;
//子视图的上面起点位置
int childTop;
//获取子控件相对父容器的对齐方式
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
//布局方向
final int layoutDirection = getLayoutDirection();
//绝对对齐方式
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
//垂直对齐方式
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
//横向居中方式来确定子控件的左边起点位置
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
//根据垂直对齐方式,来确定子视图的上面起点位置
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
//调用子视图的布局摆放
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
如果子视图是个容器,那么对应容器的onLayout方法里面基本都可以说是差不了太多,都是去遍历计算出每一个子视图的上、左的起点坐标,然后每个子视图视图继续调用layout方法去计算摆放位置
下面我们来看看布局绘制
同样还是ViewRootImpl 类中performTraversals这个方法中,经过测量、布局摆放后会调用performDraw这个方法进行布局绘制
绘制的话,是需要canvas画布,调用draw方法,看代码;
这里调用方法draw();
在draw方法中检索绘制区域,然后调用绘制drawSoftware方法;
在drawSoftware方法中,设置x、y轴的偏移量,获取Canvas画布,给画布设置一些需要的属性,然后调用View的draw的方法,开始进行绘制;
可以看到在调用View的draw方法真正开始绘制之前,是在做一些偏移值、画布的初始化、绘制区域的初始化等等工作;
往下看↓
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
} else if (mView == null) {
return;
}
//是否要重新绘制全部的视图
final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
//这里调用绘制的方法
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
}
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
......
}
private boolean draw(boolean fullRedrawNeeded) {
//面板
Surface surface = mSurface;
if (!surface.isValid()) {
return false;
}
......
//获取dirty ,表示需要绘制的区域
final Rect dirty = mDirty;
......
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
......
} else {
......
//进行绘制 如果绘制成功返回true,如果发生错误返回false
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
...
return useAsyncReport;
}
/** 如果绘制成功返回true,如果发生错误返回false
* @return true if drawing was successful, false if an error occurred
*/
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
// Canvas 画布
final Canvas canvas;
//设置X、Y轴的偏移值
int dirtyXOffset = xoff;
int dirtyYOffset = yoff;
if (surfaceInsets != null) {
dirtyXOffset += surfaceInsets.left;
dirtyYOffset += surfaceInsets.top;
}
try {
dirty.offset(-dirtyXOffset, -dirtyYOffset);
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
//获取到canvas 画布
canvas = mSurface.lockCanvas(dirty);
//设置Canvas画布像素密度
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not lock surface", e);
// Don't assume this is due to out of memory, it could be
// something else, and if it is something else then we could
// kill stuff (or ourself) for no reason.
mLayoutRequested = true; // ask wm for a new surface next time.
return false;
} finally {
//设置偏移X、Y值
dirty.offset(dirtyXOffset, dirtyYOffset); // Reset to the original value.
}
try {
......
try {
......
//调用View的draw方法,进行开始绘制
mView.draw(canvas);
......
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
......
}
return true;
}
View里面的draw方法
在View的draw方法中主要的工作是,根据绘制顺序步骤执行绘制;
绘制遍历执行的步骤顺序:
1、对View的背景进行绘制;
2、保存当前的图层;
3、绘制视图的内容;
4、对View的子View进行绘制(如果有子View);
5、绘制View的褪色的边缘,类似于阴影效果;
6、绘制装饰(例如滚动条);
7、绘制默认焦点突出显示;
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
// 第一步:对View的背景进行绘制;
int saveCount;
drawBackground(canvas);
// 第二步:保存当前的图层;
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// 第三步:绘制视图的内容;
onDraw(canvas);
// 第四步:对View的子View进行绘制(如果有子View);
dispatchDraw(canvas);
// 第五步:绘制View的褪色的边缘,类似于阴影效果;
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// 第六步:绘制装饰(例如滚动条);
onDrawForeground(canvas);
// 第七步:绘制默认焦点突出显示;
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
//下面对一些特殊的地方,还会重复做绘制步骤
boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;
float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;
// 第二步:保存当前的图层;
int paddingLeft = mPaddingLeft;
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
right += getRightPaddingOffset();
bottom += getBottomPaddingOffset();
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
// clip the fade length if top and bottom fades overlap
// overlapping fades produce odd-looking artifacts
if (verticalEdges && (top + length > bottom - length)) {
length = (bottom - top) / 2;
}
// also clip horizontal fades if necessary
if (horizontalEdges && (left + length > right - length)) {
length = (right - left) / 2;
}
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
drawTop = topFadeStrength * fadeHeight > 1.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
}
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
saveCount = canvas.getSaveCount();
int topSaveCount = -1;
int bottomSaveCount = -1;
int leftSaveCount = -1;
int rightSaveCount = -1;
int solidColor = getSolidColor();
if (solidColor == 0) {
if (drawTop) {
topSaveCount = canvas.saveUnclippedLayer(left, top, right, top + length);
}
if (drawBottom) {
bottomSaveCount = canvas.saveUnclippedLayer(left, bottom - length, right, bottom);
}
if (drawLeft) {
leftSaveCount = canvas.saveUnclippedLayer(left, top, left + length, bottom);
}
if (drawRight) {
rightSaveCount = canvas.saveUnclippedLayer(right - length, top, right, bottom);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// 第三步:绘制视图的内容;
onDraw(canvas);
// 第四步:对View的子View进行绘制(如果有子View);
dispatchDraw(canvas);
// 第五步:绘制View的褪色的边缘,类似于阴影效果;
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
// must be restored in the reverse order that they were saved
if (drawRight) {
matrix.setScale(1, fadeHeight * rightFadeStrength);
matrix.postRotate(90);
matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
if (solidColor == 0) {
canvas.restoreUnclippedLayer(rightSaveCount, p);
} else {
canvas.drawRect(right - length, top, right, bottom, p);
}
}
if (drawLeft) {
matrix.setScale(1, fadeHeight * leftFadeStrength);
matrix.postRotate(-90);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
if (solidColor == 0) {
canvas.restoreUnclippedLayer(leftSaveCount, p);
} else {
canvas.drawRect(left, top, left + length, bottom, p);
}
}
if (drawBottom) {
matrix.setScale(1, fadeHeight * bottomFadeStrength);
matrix.postRotate(180);
matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
if (solidColor == 0) {
canvas.restoreUnclippedLayer(bottomSaveCount, p);
} else {
canvas.drawRect(left, bottom - length, right, bottom, p);
}
}
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
if (solidColor == 0) {
canvas.restoreUnclippedLayer(topSaveCount, p);
} else {
canvas.drawRect(left, top, right, top + length, p);
}
}
canvas.restoreToCount(saveCount);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// 第六步:绘制装饰(例如滚动条);
onDrawForeground(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
}
//第四步:对View的子View进行绘制(如果有子View);
protected void dispatchDraw(Canvas canvas) {
}
private void drawBackground(Canvas canvas) {
//mBackground是该View的背景参数,比如背景颜色
final Drawable background = mBackground;
if (background == null) {
return;
}
//根据View四个布局参数来确定背景的边界
setBackgroundBounds();
// Attempt to use a display list if requested.
if (canvas.isHardwareAccelerated() && mAttachInfo != null
&& mAttachInfo.mThreadedRenderer != null) {
mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
final RenderNode renderNode = mBackgroundRenderNode;
if (renderNode != null && renderNode.isValid()) {
setBackgroundRenderNodeProperties(renderNode);
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
return;
}
}
//获取当前View的mScrollX和mScrollY值
final int scrollX = mScrollX;
final int scrollY = mScrollY;
//如果有值,则偏移之后重新绘制
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
//偏移位置
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
//绘制背景
void setBackgroundBounds() {
if (mBackgroundSizeChanged && mBackground != null) {
//设置背景四个参数
mBackground.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
rebuildOutline();
}
}