我们知道Android 软件每一个界面 都是Activity构成的 那么Activity 是怎么创建一个界面的呢 ?
接下来是看源码时间 过程复杂 耐心观看
setContentView(R.layout.activity_main);
这个大家再熟不过了 onCreate中调用这个就可以创建对应布局文件的界面
接下来看API源码 :
activity中:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow()得到的是一个Window对象
public Window getWindow() {
return mWindow;
}
Window是一个抽象类 不可能创建对象
那么我们找一下它的实现类 (Activity六千行代码 这是真难找啊 )。
在attach方法中:
// attach 方法是Activity通过反射实例化之后调用的第一个方法 ,之后生命周期的方法才会被调用
可以看到调用了PolicyManager的静态方法
看一下这个PolicyManager
咦 SDK看不了 代码中也调用不了 什么鬼
看一下它所在的包名 :
- com.android.internal.policy.PolicyManager
!!!这根本就不在 android.jar 里
这怎么才能找到这个包里的源码呢
用我上一篇博客说的:http://grepcode.com/ 这个源码浏览网站
找到这个PolicyManager 类里的:
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
再看一下 sPolicy 是什么
它是一个接口 被Policy实现 代码用反射构建了一个Policy对象
(看来离Window类的真面目不远了)。。。。
Policy中的makeNewWindow()方法
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
构造了一个PhoneWindow对象 这就是Window的实现类 也就是它调用了setContentView() 方法 方法内部:
判断没ContentParent 是否为null 是:调用installDecor() ,否:移除内部所有View 。
然后通过 mLayoutInflater.inflate()
LayoutInflater也是个抽象类 ,在PhoneWindow的构造方法中通过静态方法from创建的 它由哪个子类继承的还真不知道
接着说installDecor() ,就截取了开头一段
首先初始化了 mDecor ,通过generateDecor方法 ,看一下:
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
然后找这个构造方法 发现 DecorView是PhoneWindow的内部类 并且是 private final 类型
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
public DecorView(Context context, int featureId) {
super(context);
mFeatureId = featureId;
}
然后通过 generateLayut(mDecor)方法 把这个上面创建好的mDecor作为参数传递
这个方法很长 ,找到有一段:
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);
这里的layoutResource是 int值 对应我们mainfest中设置的theme主题所对应的屏幕布局
可以是R.layout.screen_custom_title;R.layout.screen_action_bar等等
现在我们知道了 DecorView 就是当前屏幕的除状态栏的区域,mContentParent对应的是里面除了ActionBar的区域
下面贴上这个图,你就可以看明白了(转自 工匠若水)
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
把contentParent 变成了 ID_ANDROID_CONTENT 对应的ViewGroup ,这是什么东西 ,看下这个 id
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
R.id.content ? 这怎么像是屏幕状态栏下方的内容区域的
id
通过findViewById给contentParent加上这么个 id
好吧 暂时不理解 必须要看一下上面提到的 decor.addView 到底干了什么 ,看一下R.layout.screen_custom_title 这个布局吧
// 文件位置:frameworks\base\core\res\res\layout (frameworks包下)
<!--
This is a custom layout for a screen.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<!-- Popout bar for action modes -->
<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/title_container"
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
android:transitionName="android:title"
style="?android:attr/windowTitleBackgroundStyle">
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
我们知道这个布局是一个屏幕的布局文件:上面的title_container是用来放自定义Title的容器,而下面的 content 就是放置我们设置的布局的容器
再看一下
generateLayout的方法:
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);
啊哦 getDecorView
把整个手机界面给了DecorView 感觉要到了见证奇迹的时候了呢!!!
看下findViewById方法的实现 :
public View findViewById(int id) {
return getDecorView().findViewById(id);
}
getDecorView 显然是当前PhoneWindow中的DecorView对象 ,通过它把id 为 content的部分给了这个contentParent 然后return
记得 PhoneWindow中的setContentView 方法是这么写的
mLayoutInflater.inflate(layoutResID, mContentParent);
而这里的inflate方法是将LayoutResID对应的布局文件加载给mContentParent对应的ViewGroup
把我们为Activity设置的布局(layoutResId)设置给mContentParent(上面installDecor方法中的generateLayout方法return得到的contentParent)
到这里 我们已经把布局给放到DecorView中 接下来安卓系统就可以为我们显示界面了
且
在onResume()刚执行之后,界面还是不可见的,只有执行完Activity.makeVisible(),DecorView才对用户可见
最后拿一张别人的Android的窗口管理框架(转自ariesjzj)