赞
踩
日常开发中,我们经常碰到需要自定义View(包括自定义View和ViewGroup)的地方,并且在面试中也需要我们熟悉或者精通自定义View。毕竟有时候我们需要实现的效果,Android本身是没有提供的,就需要我们去自定义,掌握原理可以让我们在实际使用中更加得心应手。
从Activity走起,我们日常是通过Activity的onCreate()方法中的 setContentView(R.layout.activity_main) 给Activity设置布局文件。我们一步一步的往下走,
public void setContentView(@LayoutRes int layoutResID) {
// 调用的是Window的setContentView()方法
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;
}
// 然后到Window对象去查看,看到注释
// he only existing implementation of this abstract class is android.view.PhoneWindow
// 告诉我们它是一个抽象类,有唯一的一个实现类PhoneWindow,所以我们到PhoneWindow中去查看,找到setContentView()方法
public void setContentView(int layoutResID) { if (mContentParent == null) { // 去创建一个DecorView 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==》layoutResID去解析xml布局资源 // 通过后面源码的分析,我们知道setContentView(layoutId) // layoutResID是被放在基础容器的FrameLayout中的 mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }
private void installDecor() { ... // 判断DecorView是否为空 if (mDecor == null) { // 需要去创建DecorView mDecor = generateDecor(-1); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } else { mDecor.setWindow(this); } ... // 判断mContentParent(是ViewGroup)是否为空 if (mContentParent == null) { // 根据DecorView去得到了内容容器FrameLayout mContentParent = generateLayout(mDecor); } }
protected DecorView generateDecor(int featureId) { Context context; if (mUseDecorContext) { Context applicationContext = getContext().getApplicationContext(); if (applicationContext == null) { context = getContext(); } else { context = new DecorContext(applicationContext, getContext()); if (mTheme != -1) { context.setTheme(mTheme); } } } else { context = getContext(); } // 重点关注最后一行,在这里直接根据context和featureId创建了一个DecorView return new DecorView(context, featureId, this, getAttributes()); }
protected ViewGroup generateLayout(DecorView decor) { ... // Inflate the window decor. // 定义一个Layout的资源 int layoutResource; int features = getLocalFeatures(); // 根据不同的features和其他判断条件,给layoutResource设置不同的Layout资源 if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { layoutResource = R.layout.screen_swipe_dismiss; setCloseOnSwipeEnabled(true); ... // 省略一些代码,下面省略的判断条件基本跟上面这个条件大同小异 } else { layoutResource = R.layout.screen_simple; } mDecor.startChanging(); // 解析layoutResource布局代表的基础容器,将基础容器装入DecorView mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); // 根据ID_ANDROID_CONTENT去获取主容器,根据下面从Window中拿过来的注释和代码我们可以看到 // The ID that the main layout in the XML layout file should have. // public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content; ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); ... 省略代码 // 将主容器contentParent返回 return contentParent; }
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { ...省略代码 mDecorCaptionView = createDecorCaptionView(inflater); // 解析layoutResource final View root = inflater.inflate(layoutResource, null); if (mDecorCaptionView != null) { if (mDecorCaptionView.getParent() == null) { addView(mDecorCaptionView, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } mDecorCaptionView.addView(root, new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT)); } else { // Put it below the color views. // 将layoutResource得到的View装入DecorView中 addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } mContentRoot = (ViewGroup) root; initializeElevation(); }
到此为止的图解,类结构图和视图结构图
类结构图
视图结构图
根据layoutResource的其中一个R.layout.screen_simple,我们知道每个主题设置到的布局是一个线性布局LinearLayout ,下一级就是ViewStub 涉及的ActionBar之类的,最后就是我们刚刚讲到的内容容器FrameLayout(Window中的主容器ID:com.android.internal.R.id.content,看下面代码我们就可以知道就是下面代码中FrameLayout的布局ID)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <ViewStub android:id="@+id/action_mode_bar_stub" android:inflatedId="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="?attr/actionBarTheme" /> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:foregroundInsidePadding="false" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" /> </LinearLayout>
注意:视图的绘制从DecorView开始依次递归,ViewGroup —> View的调用:measure、layout、draw
总结:首先系统会创建一个顶层的布局容器DecorView,DecorView是一个容器(ViewGroup)继承自FrameLayout,是PhoneWindow持有的实例,是所有应用程序的顶层View,在系统内部进行初始化。当DecorView创建完成之后,系统会根据应用主题特性去加载一个基础容器,不论基础容器是什么,其中都会有一个id为com.android.internal.R.id.content的FrameLayout容器,而我们通过setContentView(layoutId)设置的xml布局就是被添加到这个FrameLayout容器中。
来看看handleMessage这个类就跟网上讲的很多的不同,其实源码怎么变都是大同小异。只是以前怎么调用一些生命周期方法,现在进行了更多的抽取。
ActivityThread实现ClientTransactionHandler接口,然后实现Activity的各个生命周期方法的具体逻辑。
然后在handleMessage中调用,具体调用TransactionExecutor的execute()方法
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
// TransactionExecutor中去真正的执行Activity的生命周期方法
mTransactionExecutor.execute(transaction);
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
// message is handled.
transaction.recycle();
}
// TODO(lifecycler): Recycle locally scheduled transactions.
break;
TransactionExecutor的execute()
public void execute(ClientTransaction transaction) {
... 省略代码
// 下面两个方法最终都会走到执行生命周期方法
executeCallbacks(transaction);
executeLifecycleState(transaction);
mPendingActions.clear();
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}
TransactionExecutor的调用生命周期的方法,我们发现调用的ClientTransactionHandler的生命周期方法,这些实现是在ActivityThread中。
private void performLifecycleSequence(ActivityClientRecord r, IntArray path, ClientTransaction transaction) { final int size = path.size(); for (int i = 0, state; i < size; i++) { state = path.get(i); if (DEBUG_RESOLVER) { Slog.d(TAG, tId(transaction) + "Transitioning activity: " + getShortActivityName(r.token, mTransactionHandler) + " to state: " + getStateName(state)); } switch (state) { case ON_CREATE: mTransactionHandler.handleLaunchActivity(r, mPendingActions, null /* customIntent */); break; case ON_START: mTransactionHandler.handleStartActivity(r, mPendingActions); break; case ON_RESUME: mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */, r.isForward, "LIFECYCLER_RESUME_ACTIVITY"); break; case ON_PAUSE: mTransactionHandler.handlePauseActivity(r.token, false /* finished */, false /* userLeaving */, 0 /* configChanges */, mPendingActions, "LIFECYCLER_PAUSE_ACTIVITY"); break; case ON_STOP: mTransactionHandler.handleStopActivity(r.token, false /* show */, 0 /* configChanges */, mPendingActions, false /* finalStateRequest */, "LIFECYCLER_STOP_ACTIVITY"); break; case ON_DESTROY: mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */, 0 /* configChanges */, false /* getNonConfigInstance */, "performLifecycleSequence. cycling to:" + path.get(size - 1)); break; case ON_RESTART: mTransactionHandler.performRestartActivity(r.token, false /* start */); break; default: throw new IllegalArgumentException("Unexpected lifecycle state: " + state); } } }
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) { unscheduleGcIdler(); mSomeActivitiesChanged = true; // TODO Push resumeArgs into the activity for consideration // 回调Activity中onResume()生命周期方法 final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); ...省略代码 if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); // wm.addView(decor, l),我们需要去找到这个方法具体的实现的地方 ViewManager wm = a.getWindowManager(); // 初始化窗口属性 WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (r.mPreserveWindow) { a.mWindowAdded = true; r.mPreserveWindow = false; ViewRootImpl impl = decor.getViewRootImpl(); if (impl != null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; // 将DecorView装入窗口 wm.addView(decor, l); } else { a.onWindowAttributesChanged(l); } } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. } else if (!willBeVisible) { if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; } // Get rid of anything left hanging around. cleanUpPendingRemoveWindows(r, false /* force */); }
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ...省略代码 // 这里声明了一个ViewRootImpl对象 ViewRootImpl root; View panelParentView = null; synchronized (mLock) { // Start watching for system property changes. if (mSystemPropertyUpdater == null) { mSystemPropertyUpdater = new Runnable() { @Override public void run() { synchronized (mLock) { for (int i = mRoots.size() - 1; i >= 0; --i) { mRoots.get(i).loadSystemProperties(); } } } }; SystemProperties.addChangeCallback(mSystemPropertyUpdater); } int index = findViewLocked(view, false); if (index >= 0) { if (mDyingViews.contains(view)) { // Don't wait for MSG_DIE to make it's way through root's queue. mRoots.get(index).doDie(); } else { throw new IllegalStateException("View " + view + " has already been added to the window manager."); } // The previous removeView() had not completed executing. Now it has. } // If this is a panel window, then find the window it is being // attached to for future reference. if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { final int count = mViews.size(); for (int i = 0; i < count; i++) { if (mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); } } } // 将前面声明的root(ViewRootImpl)对象进行实例化 root = new ViewRootImpl(view.getContext(), display); // 给View设置属性 view.setLayoutParams(wparams); // 最终将对应的View添加到相应的集合中 mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try { // 最后root调用setView方法将我们传入的View,LayoutParams属性和panelParentView进行关联 root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } }
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...省略代码
// 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();
...省略代码
}
+执行ViewRootImpl的 scheduleTraversals()
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 执行这样代码,然后mTraversalRunnable的run方法会被调用
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
... 省略代码
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
// 将会在这个方法中执行我们View绘制的三大步骤
performTraversals();
...
}
}
// Ask host how big it wants to be
// 找到我们绘制流程第一步测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// 然后找到绘制流程的第二步布局
performLayout(lp, mWidth, mHeight);
// 找到我们绘制流程的最后一步,draw
performDraw();
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
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 = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
View的测量是通过 尺寸 + 模式 来保存的,将这两个值保存在MeasureSpec。
MeasureSpec是一个32位的int类型的值,前两位保存模式,后30位保存尺寸。
SpecMode(前2位) + SpecSize(后30位)
MeasureSpec中的三种模式
private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; // 不确定,父类没有约束,view可以使任意大小,一般是系统使用 public static final int UNSPECIFIED = 0 << MODE_SHIFT; // 对应的二进制 00000000 00000000 00000000 00000000 // 精确的,父类测量除了View的大小,所以我们view的大小就是specSize // 对应我们的match_content和固定大小 public static final int EXACTLY = 1 << MODE_SHIFT; // 对应的二进制 01000000 00000000 00000000 00000000 // 包裹,父容器指定一个可用大小,view最大不能超过这个容器的大小 // 对应我们的wrap_content public static final int AT_MOST = 2 << MODE_SHIFT; 10000000 00000000 00000000 00000000 // 对应的二进制
我们常用的MeasureSpec的方法:
public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
@MeasureSpecMode int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
// 将size和mode转换成MeasureSpec
// 这里意思是我们要去size的后30位和mode的前两位
// &:都为1才是1,否则为0,|:只要有一个为1就是1,否则为0
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
MODE_MASK:11000000 00000000 00000000 00000000
~MODE_MASK:00111111 11111111 11111111 11111111
public static int getMode(int measureSpec) {
//noinspection ResourceType
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
DecorView的MeasureSpec是由窗口大小和DecorView自身的LayoutParams决定。
// mWidth窗口容器的宽,lp.width我们的顶级ViewDecorView的宽
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
通过getRootMeasureSpec获取DecorView的MeasureSpec
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; }
通过前面的查找,我们知道performMeasure方法会走到View的onMeasure,因为DecorView是继承的FrameLayout,所以我们去查看FrameLayout的onMeasure方法。
FrameLayout的OnMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { ...省略代码 // 遍历所有子View for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { // 调用ViewGroup的measureChildWithMargins测量所有孩子的尺寸 // 将父容器的测量规格传递下去 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); } } } } }
ViewGroup的measureChildWithMargins
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);
}
获取子View的测量规格
// spec:父容器的测量规格 // padding:父容器已经使用的空间大小 // childDimension:子View的尺寸 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. 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); }
parentMeasureSpec \ childLayoutParams | EXACTLY | AT_MOST | UNSPECIFIED |
---|---|---|---|
dp/px | EXACTLY childSize | EXACTLY childSize | EXACTLY childSize |
match_parent | EXACTLY parentSize | AT_MOST parentSize | UNSPECIFIED 0 |
wrap_content | AT_MOST parentSize | AT_MOST parentSize | UNSPECIFIED 0 |
总结:
1、ViewGroup测量:measure() --> onMeasure()(需要测量子控件的宽高) --> setMeasuredDimension --> setMeasuredDimensionRaw(保存自己的宽高)
2、View的测量:measure() --> onMeasure() --> setMeasuredDimension() --> setMeasuredDimensionRaw(保存自己的宽高)
注意:我们平时在自定义View的时候,一定要重写OnMeasure()方法,我们从上面View的OnMeasure()方法中可以看到,其中默认调用了getDefaultSize()方法,然后从getDefaultSize的源码中可以看到,不管是AT_MOST还是EXACTLY最终返回的测量结果都是specSize,所以会造成我们在布局中写match_parent和wrap_content没有任何区别
// Window的LayoutParams,window的宽、高
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
...省略代码
// mView就是我们的DecorView
final View host = mView;
if (host == null) {
return;
}
...省略代码
// 调用View的layout方法,确定view自身的位置
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...省略代码
}
public void layout(int l, int t, int r, int b) { ...省略代码 // layout其实就是为了求解左、上、右、下的值 int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; // 通过setFrame去获取mLeft、mTop、mBottom、mRight的值 // 确定View自身的位置 boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { // 调用了onLayout方法,是一个空方法,如果是ViewGroup,就需要实现这个方法,然后确定子View的位置 onLayout(changed, l, t, r, b); ...省略代码 } ...省略代码 }
protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false; if (DBG) { Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + "," + right + "," + bottom + ")"); } if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { changed = true; // Remember our drawn bit int drawn = mPrivateFlags & PFLAG_DRAWN; int oldWidth = mRight - mLeft; int oldHeight = mBottom - mTop; int newWidth = right - left; int newHeight = bottom - top; boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight); // Invalidate our old position invalidate(sizeChanged); // 获取左、上、右、下的值 mLeft = left; mTop = top; mRight = right; mBottom = bottom; mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); mPrivateFlags |= PFLAG_HAS_BOUNDS; if (sizeChanged) { sizeChange(newWidth, newHeight, oldWidth, oldHeight); } if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) { // If we are visible, force the DRAWN bit to on so that // this invalidate will go through (at least to our parent). // This is because someone may have invalidated this view // before this call to setFrame came in, thereby clearing // the DRAWN bit. mPrivateFlags |= PFLAG_DRAWN; invalidate(sizeChanged); // parent display list may need to be recreated based on a change in the bounds // of any child invalidateParentCaches(); } // Reset drawn bit to original value (invalidate turns it off) mPrivateFlags |= drawn; mBackgroundSizeChanged = true; mDefaultFocusHighlightSizeChanged = true; if (mForegroundInfo != null) { mForegroundInfo.mBoundsChanged = true; } notifySubtreeAccessibilityStateChangedIfNeeded(); } return changed; }
总结:布局的流程
1、ViewGroup --> layout(确定自己的位置,四个点left,top,right,bottom的位置) --> onLayout(确定子view的位置)
2、View --> layout(确定自己的位置,四个点left,top,right,bottom的位置)
在ViewRootImpl中找到 performDraw() 方法,然后在其中找到 draw() 方法。
...省略代码
try {
// 找到draw方法
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
}
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
...省略代码
ViewRootImpl的 draw(),然后在方法中找到drawSoftware()
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
最终在drawSoftware()方法中找到mView.draw(canvas),然后走到View的draw()方法中去
View的draw()
public void draw(Canvas canvas) { // 关键代码,绘制流程 /* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) */ // Step 1, draw the background, if needed int saveCount; drawBackground(canvas); // skip step 2 & 5 if possible (common case) final int viewFlags = mViewFlags; boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0; boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0; if (!verticalEdges && !horizontalEdges) { // Step 3, draw the content onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); drawAutofilledHighlight(canvas); // Overlay is part of the content and draws beneath Foreground if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); } // Step 6, draw decorations (foreground, scrollbars) onDrawForeground(canvas); // Step 7, draw the default focus highlight drawDefaultFocusHighlight(canvas); if (debugDraw()) { debugDrawFocus(canvas); } // we're done... return; } }
我们查看ViewGroup对View的绘制,在ViewGroup中搜索dispatchDraw(canvas),ViewGroup为我们实现了这个方法。
// 截取一部分代码,还有其他很多源码,有兴趣可以自己观摩 for (int i = 0; i < childrenCount; i++) { while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) { final View transientChild = mTransientViews.get(transientIndex); if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || transientChild.getAnimation() != null) { // 遍历所有的子View,然后调用drawChild进行子View的绘制 more |= drawChild(canvas, transientChild, drawingTime); } transientIndex++; if (transientIndex >= transientCount) { transientIndex = -1; } } final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { // 遍历所有的子View,然后调用drawChild进行子View的绘制 more |= drawChild(canvas, child, drawingTime); } }
ViewGroup的drawChild()
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
// 最终又调用了View的draw方法
return child.draw(canvas, this, drawingTime);
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。