一、View绘制流程
2020-03-04 本文已影响0人
徒步青云
1、Activity布局添加流程
1、创建顶层布局容器DecorView,它被PhoneWindow类所持有
2、在顶层布局DecorView中加载基础布局SubDecor,SubDecor一般包含两部分:title和content
3、将自定义布局添加到基础布局SubDecor中
new了一个DecorView,获取其中id为content的控件mContentParent,将subDecor添加到mContentParent,将自定义布局文件添加到subDecor
image.png源码分析:
//MainActivity.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(int layoutResID)
}
//AppCompatActivity.java
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
//AppCompatDelegate.java
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
//create静态方法返回AppCompatDelegateImpl对象
//第二个参数将activity中的mWindow对象,赋值给AppCompatDelegateImpl中mWindow变量
//而activity中的mWindow对象,是在attach方法中被new成PhoneWindow
return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
}
//AppCompatDelegateImpl.java
public void setContentView(int resId) {
// ***重点***
//1、创建SubDecor,并将其添加到DecorView中
ensureSubDecor();
//2、获得id为content的FrameLayout
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
//3、将自定义布局添加到content布局中
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
//.......省略设置标题、content.requestLayout等内容
mSubDecorInstalled = true;
}
}
private ViewGroup createSubDecor() {
//.......省略ActionBar风格设置
// Now let's make sure that the Window has installed its decor by retrieving it
//如果DecorView不存在,则创建它,这里需要注意一下
mWindow.getDecorView();
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
//.......省略部分内容:根据不同风格、属性,为subDecor设置不同的布局文件
if (mDecorContentParent == null) {
//获取title控件
mTitleView = (TextView) subDecor.findViewById(R.id.title);
}
final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
R.id.action_bar_activity_content);
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
//如果PhoneWindow中已存在id为content的容器,则将容器中所有子控件转移到DecorView中
if (windowContentView != null) {
while (windowContentView.getChildCount() > 0) {
final View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}
windowContentView.setId(View.NO_ID);
contentView.setId(android.R.id.content);
}
//将subDecor设置给mWindow
mWindow.setContentView(subDecor);
return subDecor;
}
//PhoneWindow.java
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//new了一个DecorView,它继承至FrameLayout
mDecor = generateDecor(-1);
//........
} else {
mDecor.setWindow(this);
//........
}
if (mContentParent == null) {
//mContentParent控件id为content
mContentParent = generateLayout(mDecor);
//........
}
}
protected ViewGroup generateLayout(DecorView decor) {
//省略layoutResource赋值流程
mDecor.startChanging();
//将layoutResource布局文件inflate到mDecor中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//省略后续操作
return contentParent;
}
//mWindow.setContentView(subDecor),其实是调用PhoneWindow的setContentView方法,将subDecor传入
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//用于mContentParent已经在getDecorView->installDecor中被赋值,所以会调用到下面这个方法
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
//将subDecor添加到DecorView中
mContentParent.addView(view, params);
}
//......
}
image.png
image.png
Activity的创建离不开ActivityThread。
ActiveThread是每一个应用程序所在进程的主线程。
//ActivityThread有一个ArrayList存储启动的 Activity列表。
final ArrayList<ActivityClientRecord> mRelaunchingActivities
= new ArrayList<ActivityClientRecord>();
ActivityClientRecord存储启动的Activity相关信息。
https://blog.csdn.net/joedan0104/article/details/82857450
2、View的绘制流程
ActivityThread.handleResumeActivity()
-->WindowManagerImpl.addView()
-->WindowManagerGlobal.addView()
-->ViewRootImpl.addView()
-->ViewRootImpl.requestLayout()
-->ViewRootImpl.scheduleTraversals()
-->ViewRootImpl.doTraversal()
-->ViewRootImpl.performTraversal()
-->进入View绘制流程
1、绘制入口
ActivityThread.handleResumeActivity
->WindowManagerImpl.addView(decorView,layoutParams)
->WindowManagerGlobal.addView()//会创建ViewRootImpl对象
2、绘制的类及方法
ViewRootImpl.setView(decorView,layoutParams,parentView)
->requestLayout()
->scheduleTraversals()
->doTraversal()
->performTraversals()
3、绘制三大步骤
测量:ViewRootImpl.performMeasure
布局:ViewRootImpl.performLayout
绘制:ViewRootImpl.performDraw
3、UI绘制详细步骤
1、测量performMeasure
view.measure->view.onMeasure->view.setMeasuredDimension->setMeasuredDimensionRaw
2、布局performLayout
view.layout->view.onLayout
3、绘制performDraw
ViewRootImpl.draw(fullRedrawNeeded)->ViewRootImpl.drawSoftware->view.draw(Canvas)
View测量:
MeasureSpec是一个32位int值,其中前2位表示模式,后30位表示尺寸大小。
相关方法:
MeasureSpec.getMode()
MeasureSpec.getSize()
MeasureSpec.makeMeasureSpec(size, mode)
模式 | 含义 |
---|---|
AT_MOST | 父容器指定一个可用大小,View的大小不能超过这个值,LayoutPamras、wrap_content |
EXACTLY | 父容器检测出View的大小,View的大小就是SpecSize、LayoutPamras、match_parent、固定大小 |
UNSPECIFIED | 父容器不对View做任何限制,系统内部使用 |