前面我们分别介绍了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.java
public 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中了。