Android的Activity中setContentView到底经历了什么?
前面我们分别介绍了Android中的事件分发和视图绘制的核心流程。也掺杂着setContentView介绍了下,今天我们简单扼要专门分析下,希望有个更直观、清晰的认识。(趁这几天不太忙,多多总结…)
1、LayoutInflater的inflate(params …)参数
LayoutInflater 根据注释,意思是“把一个xml文件实例化成一个View对象”。
平时经常会使用两种写法获取LayoutInflater的实例,虽说归根结底还是一种。
1、
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
2、
LayoutInflater layoutInflater = LayoutInflater.from(context);
其实两者一样,2是1的简化版,看源码。
LayoutInflater.javapublic static LayoutInflater from(Context context) { LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (LayoutInflater == null) { throw new AssertionError("LayoutInflater not found."); } return LayoutInflater;}
比如,在Activity中的快速使用。
LayoutInflater.from(this).inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)。
第一个参数resource,是表示要加载的布局文件。
第二个参数root,表示加载的resource是否需要一个父布局。
第三个参数attachToRoot,表示resource是否加载在父布局中。
对于attachToRoot有几种情况
- root 为null时,attachToRoot将不会起作用
- root 不为null,attachToRoot为true时,给resource一个root父布局,并且resource中的layout_width等参数生效。
- root 不为null,attachToRoot为false时,root不生效,只是将resource加载成一个View,但含有layout属性,即子视图的layout属性生效。
- 没有attachToRoot参数时,root !=null时,attachToRoot默认为true。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) { return inflate(parser, root, root != null);}
2、inflate使用
根据上面的参数分析,我们试着去用示例验证下。毕竟这样才不容易忘记,理解也更深入。
btn_layout.xml
<?xml version="1.0" encoding="utf-8"?><Button xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="200dp" android:layout_height="200dp" android:orientation="vertical" android:text="button">Button>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.hds.viewdraw.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/app_name"/>LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LinearLayout mainLayout = (LinearLayout) findViewById(R.id.main_layout); View view = LayoutInflater.from(this).inflate(R.layout.btn_layout, null); mainLayout.addView(view); }}
1、根据上述代码的运行结果是,Button的布局并没有根据指定的200dp展示,是linearLayout的vertical的默认子布局layoutparams:width=match_parent,height=wrap_content。
2、MainActivity改为
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LinearLayout mainLayout = (LinearLayout) findViewById(R.id.main_layout); View view = LayoutInflater.from(this).inflate(R.layout.btn_layout, mainLayout,false); mainLayout.addView(view); }}
button设置的layout生效,button被linearlayout以addView的形式加载。
3、对于2中当前的情况,有种简单写法,直接加载到root中,结果都一样。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LinearLayout mainLayout = (LinearLayout) findViewById(R.id.main_layout); LayoutInflater.from(this).inflate(R.layout.btn_layout, mainLayout,true); }}
3、setContentView
下面是针对目前内容的简化代码(真正伪代码,真实可不是这样子的啊),着重过程、理解关系。
public class Activity extends XXX,Window.Callback,XXX { private Window mWindow; final void attach(Context context, ActivityThread aThread,XXX, Window window) { mWindow = new PhoneWindow(this, window); mWindow.setCallback(this); } public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); } public Window getWindow() { return mWindow; }}
public class PhoneWindow extends Window XXX{ private LayoutInflater mLayoutInflater; // This is the top-level view of the window, containing the window decor. private DecorView mDecor; public PhoneWindow(Context context) { super(context); mLayoutInflater = LayoutInflater.from(context); } @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(); } mLayoutInflater.inflate(layoutResID, mContentParent); } private void installDecor() { mForceDecorInstall = false; if (mDecor == null) { mDecor = generateDecor(-1); } else { mDecor.setWindow(this); } if (mContentParent == null) { mContentParent = generateLayout(mDecor); } } protected DecorView generateDecor(int featureId) { return new DecorView(context, featureId, this, getAttributes()); } protected ViewGroup generateLayout(DecorView decor) { return (ViewGroup)findViewById(ID_ANDROID_CONTENT); }}
DecorView extends FrameLayout,所以是一个ViewGroup,且里面嵌套有个id为content的FrameLayout布局。
由上,setContentView(layoutResID)就是在contentView的基础上,使用mLayoutInflater.inflate(layoutResID, mContentParent)把layoutResID加载成一个View,直接添加到mContentParent中了。
更多相关文章
- .Net程序员玩转Android开发---(12)ListView显示数据
- Android动态加载jar、apk的实现
- Android源码阅读分析:从Activity开始(二)——加载布局
- Android(安卓)H5和App交互以及打开图库上传图片并显示
- 浅谈Android开机启动速度优化(含应用程序启动速度优化)
- One省电卫士 - Android内核级省电App
- Android(安卓)WebView实现离线加载功能
- mybatisplus的坑 insert标签insert into select无参数问题的解决
- Python技巧匿名函数、回调函数和高阶函数