Android UI绘制流程(一)
2017-05-03 18:21
387 查看
本文将介绍Android UI的绘制流程。简单来说就是Android的界面是经过怎样的步骤来显示出来的。
我们一般都是创建Activity。然后在Activity的onCreate()方法中通过setContentView()来设置自己的布局。那么:
设置的布局加载到哪里了呢?
Activity和布局之间有什么样的关系呢?
1 首先从Activity的setContentView()方法开始分析。
可以看出在Activity的setContent()方法中,调用了
而getWindow()方法返回的是Window对象
也就是说在Activity的setContentView()方法调用的是Window的setContentView()方法。
2 下面来看Window的setContentView()方法。
可以看出Window的setContentView()方法是个抽象的方法。那么Window的实现类是谁呢?从Window类的注释中可以看出:
Window类的唯一的实现类是PhoneWindow,接下来打开PhoneWindow来看在PhoneWindow中的setContentView()方法。我们可以发现根本就没有Link,全局也搜索不出这个类,说明这个类时隐藏的。那只好打开 SDK中的source文件夹中去搜索这个类。打开后:
3 PhoneWindow的setContentView()方法。
3.1可以看出当mContentParent = null时,会调用installDecor()方法。
首先看下mContentParen
f75f
t 是个什么鬼
这个注释说的很清晰。用我自己的话来说就是这个mContentParent就是Window用来显示内容的view.这个view有可能是mDecor也可能是mDecor的子类。
擦,那mDecor又是个什么东东?
从注释中可以看出,mDecor是Window中最高水平的View。(在后文中可以看出这个mDecor就是一个FrameLayout)
在setContentView(View view, ViewGroup.LayoutParams params) 方法中:
有这一句:
哦,也就是说当mContentParent 为null时就去创建mDecor了?
3.2下面看PhoneWnidow中的installDecor()方法。(what a fuck? why this method is so long?)
这段代码有点长 我只复制了一部分。首先看这一段
当mDecor==null时,会调用generateDecor()方法。
3.2.1接下来看generateDecor()方法
可以看出在generateDecor()方法中创建了DecorView对象。那么DecorView到底是个什么呢?
原来这个DecorView就是一个Fragment。是PhoneWindow中的一个内部类。
接着看3.2中的这一段代码
这段代码调用了generateLayout()方法。
3.2.2接下来看generateLayout()方法。
这个方法有点长我截取几部分
1 首先获取主题的样式
通过获得的样式来设置属性,下面的属性仅仅是一部分。
那么都获取了哪些属性呢?比如上面的这段。就是获取了是否显示标题栏。也就是屏幕最顶端的标题栏。并且如果标题栏不显示,那么ActionBar也不显示。
获取了主题的样式和属性后,接着
2 获取布局Id。
上面这段代码就是根据之前的样式所设置的属性来给布局id layoutResource来赋值。
可以看到有两种layoutResource被赋值的情况,如:
那么这两种布局对应的是什么样的呢?打开源码可以看到:
R.layout.screen_simple
R.layout.screen_title
也就是说会根据不同主题样式来加载不同的布局文件。而这两个布局文件都有一个id为content的FramLayout。
3 把layoutResource对应的布局添加到mDecor中
至此 mDecor做为DecorView的对象,已经初始化完成了。但是
mDecor和我们在Activity中设置的布局id是怎么联系到一起的呢?
在setContentView(int layoutResID)方法中会调用installDecor()方法,在installDecor()方法中,用mDecor来构造mContentParent。
在generateLayout(DecorView decor)方法中会有如下这一段:
这段代码中contentParent就是最后返回的ViewGroup.也就是说这段代码执行后,将会有:
mContentParent = conentParent。而contentParent是mDecor中的id为content的FrameLayout。
在setContentView(int layoutResID)方法中
至此,自己设置的资源文件id已经添加到id为content的FramLayout中了。
用图片来表示:
下面在看文章开头的两个问题:
设置的布局加载到哪里了呢?
从上图可以看出,设置的布局layoutResID会添加到DecorView中的FramLayout中。
Activity和布局之间有什么样的关系呢?
1 Activity通过Window的API来完成界面的绘制
2 PhoneWindow是Window的子类
3 PhoneWindow中包含一个内部类DecorView,它是一个FramLayout。
4 Activity对应的布局会添加到即为DecorView中所包含的一个FramLayout中。
我们一般都是创建Activity。然后在Activity的onCreate()方法中通过setContentView()来设置自己的布局。那么:
设置的布局加载到哪里了呢?
Activity和布局之间有什么样的关系呢?
1 首先从Activity的setContentView()方法开始分析。
public void setContentView(View view) { getWindow().setContentView(view); initWindowDecorActionBar(); }
可以看出在Activity的setContent()方法中,调用了
getWindow().setContentView(view);
而getWindow()方法返回的是Window对象
public Window getWindow() { return mWindow; }
也就是说在Activity的setContentView()方法调用的是Window的setContentView()方法。
2 下面来看Window的setContentView()方法。
public abstract void setContentView(@LayoutRes int layoutResID);
可以看出Window的setContentView()方法是个抽象的方法。那么Window的实现类是谁呢?从Window类的注释中可以看出:
* <p>The only existing implementation of this abstract class is * android.view.PhoneWindow, which you should instantiate when needing a * Window. */ public abstract class Window {
Window类的唯一的实现类是PhoneWindow,接下来打开PhoneWindow来看在PhoneWindow中的setContentView()方法。我们可以发现根本就没有Link,全局也搜索不出这个类,说明这个类时隐藏的。那只好打开 SDK中的source文件夹中去搜索这个类。打开后:
3 PhoneWindow的setContentView()方法。
@Override public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent == null) { 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 { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
3.1可以看出当mContentParent = null时,会调用installDecor()方法。
首先看下mContentParen
f75f
t 是个什么鬼
// This is the view in which the window contents are placed. It is either // mDecor itself, or a child of mDecor where the contents go. private ViewGroup mContentParent;
这个注释说的很清晰。用我自己的话来说就是这个mContentParent就是Window用来显示内容的view.这个view有可能是mDecor也可能是mDecor的子类。
擦,那mDecor又是个什么东东?
// This is the top-level view of the window, containing the window decor. private DecorView mDecor;
从注释中可以看出,mDecor是Window中最高水平的View。(在后文中可以看出这个mDecor就是一个FrameLayout)
在setContentView(View view, ViewGroup.LayoutParams params) 方法中:
有这一句:
if (mContentParent == null) { installDecor(); }
哦,也就是说当mContentParent 为null时就去创建mDecor了?
3.2下面看PhoneWnidow中的installDecor()方法。(what a fuck? why this method is so long?)
private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } if (mContentParent == null) { mContentParent = generateLayout(mDecor); }
这段代码有点长 我只复制了一部分。首先看这一段
if (mDecor == null) { //3.2.1 mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } }
当mDecor==null时,会调用generateDecor()方法。
3.2.1接下来看generateDecor()方法
protected DecorView generateDecor() { return new DecorView(getContext(), -1); }
可以看出在generateDecor()方法中创建了DecorView对象。那么DecorView到底是个什么呢?
private final class DecorView extends FrameLayout
原来这个DecorView就是一个Fragment。是PhoneWindow中的一个内部类。
接着看3.2中的这一段代码
if (mContentParent == null) { mContentParent = generateLayout(mDecor);
这段代码调用了generateLayout()方法。
3.2.2接下来看generateLayout()方法。
这个方法有点长我截取几部分
1 首先获取主题的样式
TypedArray a = getWindowStyle();
通过获得的样式来设置属性,下面的属性仅仅是一部分。
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE); } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) { // Don't allow an action bar if there is no title. requestFeature(FEATURE_ACTION_BAR); }
那么都获取了哪些属性呢?比如上面的这段。就是获取了是否显示标题栏。也就是屏幕最顶端的标题栏。并且如果标题栏不显示,那么ActionBar也不显示。
获取了主题的样式和属性后,接着
2 获取布局Id。
int layoutResource;
else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { // If no other features and not embedded, only need a title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { layoutResource = a.getResourceId( R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar); } else { layoutResource = R.layout.screen_title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. layoutResource = R.layout.screen_simple; // System.out.println("Simple!"); }
上面这段代码就是根据之前的样式所设置的属性来给布局id layoutResource来赋值。
可以看到有两种layoutResource被赋值的情况,如:
layoutResource = R.layout.screen_title;
layoutResource = R.layout.screen_simple;
那么这两种布局对应的是什么样的呢?打开源码可以看到:
R.layout.screen_simple
<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>
R.layout.screen_title
<com.android.internal.widget.ActionBarOverlayLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/decor_content_parent" android:layout_width="match_parent" android:layout_height="match_parent" android:splitMotionEvents="false" android:theme="?attr/actionBarTheme"> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" style="?attr/actionBarStyle" android:transitionName="android:action_bar" android:touchscreenBlocksFocus="true" android:gravity="top"> <com.android.internal.widget.ActionBarView android:id="@+id/action_bar" android:layout_width="match_parent" android:layout_height="wrap_content" style="?attr/actionBarStyle" /> <com.android.internal.widget.ActionBarContextView android:id="@+id/action_context_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone" style="?attr/actionModeStyle" /> </com.android.internal.widget.ActionBarContainer> <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar" android:layout_width="match_parent" android:layout_height="wrap_content" style="?attr/actionBarSplitStyle" android:visibility="gone" android:touchscreenBlocksFocus="true" android:gravity="center"/> </com.android.internal.widget.ActionBarOverlayLayout>
也就是说会根据不同主题样式来加载不同的布局文件。而这两个布局文件都有一个id为content的FramLayout。
3 把layoutResource对应的布局添加到mDecor中
View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
至此 mDecor做为DecorView的对象,已经初始化完成了。但是
mDecor和我们在Activity中设置的布局id是怎么联系到一起的呢?
在setContentView(int layoutResID)方法中会调用installDecor()方法,在installDecor()方法中,用mDecor来构造mContentParent。
if (mContentParent == null) { mContentParent = generateLayout(mDecor);
在generateLayout(DecorView decor)方法中会有如下这一段:
View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
这段代码中contentParent就是最后返回的ViewGroup.也就是说这段代码执行后,将会有:
mContentParent = conentParent。而contentParent是mDecor中的id为content的FrameLayout。
/** * 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;
在setContentView(int layoutResID)方法中
mLayoutInflater.inflate(layoutResID, mContentParent);
至此,自己设置的资源文件id已经添加到id为content的FramLayout中了。
用图片来表示:
下面在看文章开头的两个问题:
设置的布局加载到哪里了呢?
从上图可以看出,设置的布局layoutResID会添加到DecorView中的FramLayout中。
Activity和布局之间有什么样的关系呢?
1 Activity通过Window的API来完成界面的绘制
2 PhoneWindow是Window的子类
3 PhoneWindow中包含一个内部类DecorView,它是一个FramLayout。
4 Activity对应的布局会添加到即为DecorView中所包含的一个FramLayout中。
相关文章推荐
- Android UI绘制流程(二)
- Android UI绘制流程(一)----布局的加载
- Android——UI(一):UI绘制流程
- Android自定义View专题一 UI绘制流程
- Android-UI控件的绘制流程以及自定义控件的具体操作
- Android UI绘制流程
- 源码分析android的UI绘制流程
- Android-UI控件的绘制流程以及自定义控件的具体操作
- Android UI的绘制流程
- Android中View绘制流程以及invalidate()等相关方法分析
- Android中View绘制流程以及invalidate()等相关方法分析
- Android中View绘制流程以及invalidate()等相关方法分析
- Android研究-GUI框架-Android的View的简单绘制流程
- Android View绘制流程以及invalidate()等相关方法分析
- Android中View绘制流程以及invalidate()等相关方法分析
- Android中View绘制流程以及invalidate()等相关方法分析
- Android中View绘制流程以及invalidate()等相关方法分析
- 从Alarm看Android上层UI到内核代码的流程分析
- Android学习札记15:对Android中View绘制流程的一些理解
- Android中View绘制流程以及invalidate()等相关方法分析