从setContentView()源码看起
2.1 Activity::setContentView()
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;
}
1
2
3
4
5
6
7
2.2 mWindow的初始化是在Activity的attach方法中执行的。了解过Activity启动流程,我们知道:attach方法是在Activity启动流程中,ActivityThread::performLaunchActivity()方法中被调用的,是在Activity被创建后,完成初始化操作的。
//android.app.Activity#attach()
final void attach(Context context, ActivityThread aThread, ... Window window ...) {
...
//这里进行mWindow的初始化,可以看到Activity中的Window实现类是PhoneWindow,
//目前为止,PhoneWindow也是Window的唯一实现类
mWindow = new PhoneWindow(this, window, activityConfigCallback);
//这里是给Window设置了WindowManager,WindowManager是通过IPC获取的系统服务,
//WindowManager只是一个接口类型,具体实现是WindowManagerImpl类,
//当然WindowManagerImpl又将实际的逻辑实现交给了WindowManagerGlobal类
mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
//Activity持有的WindowManager也是从Window中拿过来的
mWindowManager = mWindow.getWindowManager();
....
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2.3 继续看PhoneWindow的setContentView()实现代码
@Override
public void setContentView(int layoutResID) {
//mContentParent其实就是android.R.id.content布局对应的实际展示的内容
if (mContentParent == null) {
//这个方法时构造一个顶层的DecorView对象,其实是直接通过new DecorView产生的实例对象,并赋值给mDecor变量
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//第二次调用setContentView()方法时走这里,会先remove掉所有的子View再通过inflate进行加载布局
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();
}
//这个变量,会在requestFeature()方法调用时判断时机是否正确,
//如果实在setContentView之后调用的,会抛出"requestFeature() must be called before adding content"的异常
mContentParentExplicitlySet = true;
}
//installDecorView():构造Activity视图框架的根视图,并通过LayoutInflater加载mContentParent,
//我们一般操作的setContentView其实就是将布局展示到了mContentParent中
private void installDecor() {
...
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//DecorView已经构造好了,可以从DecorView中通过findViewById的方式实例化mContentParent对象了
mContentParent = generateLayout(mDecor);
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2.4 generateLayout(mDecor)也是获取mContentParent对象的关键方法
protected ViewGroup generateLayout(DecorView decor) {
...
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
layoutResource = R.layout.screen_progress;
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
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;
}
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
layoutResource = R.layout.screen_simple;
}
....
//mDecorView虽然已经初始化了,但是他的布局还未加载,通过上面对features变量值的一堆if-else判断,
//获取到对应的feature值的布局文件,再通过inflater对象加载布局
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//这里获取的就是mContentParent对象,findViewById是View的方法,这里是间接调用了DecorView的findViewById方法,
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
return contentParent;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2.5 查找ID_ANDROID_CONTENT变量,可以看到:其值是android.R.id.content
/**
* 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;
1
2
3
4
2.6 另外: DecorView加载布局文件资源的方法是:onResourcesLoaded()
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
...
//这里inflate方法的root直接传null,的确这已经是跟布局了,肯定是没有父View可以传了
final View root = inflater.inflate(layoutResource, null);
...
}
从setContentView()源码看起的更多相关文章
- 从源码看Azkaban作业流下发过程
上一篇零散地罗列了看源码时记录的一些类的信息,这篇完整介绍一个作业流在Azkaban中的执行过程,希望可以帮助刚刚接手Azkaban相关工作的开发.测试. 一.Azkaban简介 Azkaban作为开 ...
- 解密随机数生成器(二)——从java源码看线性同余算法
Random Java中的Random类生成的是伪随机数,使用的是48-bit的种子,然后调用一个linear congruential formula线性同余方程(Donald Knuth的编程艺术 ...
- 从源码看Android中sqlite是怎么通过cursorwindow读DB的
更多内容在这里查看 https://ahangchen.gitbooks.io/windy-afternoon/content/ 执行query 执行SQLiteDatabase类中query系列函数 ...
- 从源码看Android中sqlite是怎么读DB的(转)
执行query 执行SQLiteDatabase类中query系列函数时,只会构造查询信息,不会执行查询. (query的源码追踪路径) 执行move(里面的fillwindow是真正打开文件句柄并分 ...
- 从Chrome源码看浏览器的事件机制
.aligncenter { clear: both; display: block; margin-left: auto; margin-right: auto } .crayon-line spa ...
- 从Chrome源码看JS Array的实现
.aligncenter { clear: both; display: block; margin-left: auto; margin-right: auto } .crayon-line spa ...
- 从源码看JDK提供的线程池(ThreadPoolExecutor)
一丶什么是线程池 (1)博主在听到线程池三个字的时候第一个想法就是数据库连接池,回忆一下,我们在学JavaWeb的时候怎么理解数据库连接池的,数据库创建连接和关闭连接是一个比较耗费资源的事情,对于那些 ...
- 从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计
使用微信小程序开发已经很长时间了,对小程序开发已经相当熟练了:但是作为一名对技术有追求的前端开发,仅仅熟练掌握小程序的开发感觉还是不够的,我们应该更进一步的去理解其背后实现的原理以及对应的考量,这可能 ...
- 从微信小程序开发者工具源码看实现原理(四)- - 自适应布局
从前面从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计可以知道,小程序大部分是通过web技术进行渲染的,也就是最终通过浏览器的dom tree + cssom来生成渲染树:既然最终是通 ...
随机推荐
- python之pymysql
PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库,Python2中则使用mysqldb. 安装: pip3 install PyMySQL 常用参数: pymysq ...
- Linux分布式消息队列RocketMQ部署与监控--双Master
环境准备:CentOS_6.5_x64 IP: 192.168.0.249 dbTest249 Master1 IP: 192.168.0.251 webTest251 Master2 下载 ali ...
- ThinkPHP依赖注入
D:\wamp64\www\thinkphp5.1\tp5.1\application\index\controller\Demo1.php文件 <?php namespace app\inde ...
- Delphi CheckBox组件
- Centos7 下Yum安装OpenLdap
网上的教程一大堆,也没用具体说明版本,所以很多操作方法都不一样,把我踩过的坑记录下来 环境: Centos7 OpenLdap 2.4.44 openldap新版本和老版本的配置方法差别特别大 安装步 ...
- 使用 SignalR 实现推送功能
百度搜索:使用 SignalR 实现推送功能
- detect 导图
https://www.zhihu.com/question/356551927/answer/919612766 知乎上看到的,不错
- solr创建core
创建Core的两种方法: 第一种方法: 1.打开dos命令窗口,切换目录到${solr.home}\bin,然后输入:solr create -c corename之后回车: 2.打开solr安装文件 ...
- html背景图不随滚轮滚动,而且按住Ctrl并滚动滚轮时,图片不会变大缩小,就像百度的首页一样
之前在百度知道我提问过这一个问题,后来解决了.不过好多人来问我时怎么解决的,源码.其实很简单.这里我贴一下代码.有需要的小伙伴不用再加我qq了,直接来这里取吧. 里面的图片是我随便找的. <!D ...
- python广度遍历文件夹
import os from collections import deque path = r"E:\Python" queue = deque([])#队列 queue.app ...