简单来说两步走:
1.设置xml布局 始于setContentView(R.layout.activity_main)
2.基于布局绘制 始于ViewRootImpl.scheduleTraversals()
1.设置xml布局
在我们自己的MainActivity里面加载xml的布局setContentView(R.layout.activity_main);
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
进入父类AppCompatActivity处理
AppCompatActivity:
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
getDelegate()指向的是AppCompatDelegateImplV9,其是V11,V14,V23的父类
AppCompatDelegateImplV9:
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
从上可知,android.R.id.content的控件是mSubDecor的子View,从inflate(resId, contentParent),跟进源码看,只要contentParent不为null(肯定不为null),那么就将我们的xml布局添加到contentParent里面,父子控件链路:mSubDecor -->android.R.id.content -->我们的xml。
Q : mSubDecor 是什么?但是肯定不是DecorView
AppCompatDelegateImplV9:
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
...省略 基于主题去设置窗体Window的属性,是否显示actionbar、title、Overlay 相关代码....
mWindow.getDecorView();
// 基于不同主题设置不同的系统自带的xml文件,这里只以abc_screen_simple为例
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
mWindow.setContentView(subDecor);
return subDecor;
}
mSubDecor其实是基于主题,inflate添加进来的一个容器,其xml名为abc_screen_simple.xml(这只是其中某种情况的例子)
PhoneWindow:这是window的唯一子类
@Override
public void setContentView(View subDecor) {
if (mContentParent == null) {
if (mDecor == null) {
mDecor = new DecorView(context, featureId, this, getAttributes());
mContentParent = getDecorView().findViewById(com.android.internal.R.id.content);
}
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
...
} else {
mContentParent.addView(view, params);
}
...省略...
}
Q : com.android.internal.R.id.content与android.R.id.content是同一个控件吗? 肯定不是
使用Layout Inspector 配合debug调试发现:
- mContentParent的id是com.android.internal.R.id.content, 对应上图中的FrameLayout
- android.R.id.content对应的是ContentFrameLayout。
基于布局绘制
绘制的入口:
ActivityThread: 绘制的入口方法
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
// 精简无关代码
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
}
绘制关联:
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
WindowManagerGlobal:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root= new ViewRootImpl(view.getContext(), display);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
//
}
}
}
ViewRootImpl:做了精简 这个类是View和WindowManager之间实现关联的桥梁类
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
requestLayout();
}
}
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
// Traversals 中文意思:遍历
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
}
private void performTraversals() {
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
if (didLayout) {
performLayout(lp, mWidth, mHeight);
......
performDraw();
}
}
进入到measure layout draw就涉及到View和ViewGroup的测量、布局和绘制了。只需要注意ViewGroup的真正绘制逻辑是在disptchDraw方法里。