Android View原理(1)-LayoutInflater原理

简述

Android系统中,加载布局除了使用setContentView()方法之外,还可以使用LayoutInfalter手动加载布局。
LayoutInflater的使用很简单,首先需要获取LayoutInflater的实例,有以下两种方法

 
     
1
2
 
     
LayoutInflater layoutInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);


 
     
1
2
3
4
5
6
7
8
9
10
 
     
LayoutInflater layoutInflater = LayoutInflater.from(context);
// 其详细定义如下
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;
}

其中第二种是对第一种的安全封装。

然后,调用也很简单

 
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
     
/**
* Inflate a new view hierarchy from the specified xml resource. Throws
* { @link InflateException} if there is an error.
*
* @param resource ID for an XML layout resource to load (e.g.,
* <code>R.layout.main_page</code>)
* @param root Optional view to be the parent of the generated hierarchy.
* @return The root View of the inflated hierarchy. If root was supplied,
* this is the root View; otherwise it is the root of the inflated
* XML file.
*/
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}

resource,就是要加载的布局资源id;
root,则表示是否将加载的布局附属到root下面。
注意返回值,如果root不为空返回的是root布局;否则返回的加载的布局。

上面的inflate调用如下函数

 
     
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
 
     
/**
* Inflate a new view hierarchy from the specified xml resource. Throws
* { @link InflateException} if there is an error.
*
* @param resource ID for an XML layout resource to load (e.g.,
* <code>R.layout.main_page</code>)
* @param root Optional view to be the parent of the generated hierarchy (if
* <em>attachToRoot</em> is true), or else simply an object that
* provides a set of LayoutParams values for root of the returned
* hierarchy (if <em>attachToRoot</em> is false.)
* @param attachToRoot Whether the inflated hierarchy should be attached to
* the root parameter? If false, root is only used to create the
* correct subclass of LayoutParams for the root view in the XML.
* @return The root View of the inflated hierarchy. If root was supplied and
* attachToRoot is true, this is root; otherwise it is the root of
* the inflated XML file.
*/
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}

也可以直接调用这个带三个参数的inflate函数。

  1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。
  2. 如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。
  3. 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。(试一下示例代码中infalter.inflate(R.layout.button_layout, main_layout, false)的这种情况,个人理解是infalte时认为是有父布局(这样layout_width和layout_height设置是有效的),但实际上又没有添加到父布局中。)

示例

下面根据一个示例进行说明:InflaterSample(代码地址)
示例为用LayoutInfalter手动加载一个button布局。部分代码如下:
button_layout.xml

 
     
1
2
3
4
5
6
7
8
 
     
<?xml version="1.0" encoding="utf-8"?>
<!-- 可以尝试将layout_width和height修改成别的值看下效果 -->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content"
android:text= "Button" >
</Button>

主布局activity_main.xml

 
     
1
2
3
4
5
6
7
 
     
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id= "@+id/main_layout"
android:layout_width= "match_parent"
android:layout_height= "match_parent" >
</LinearLayout>

MainActivity

 
     
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
 
     
public class MainActivity extends Activity {
private LinearLayout main_layout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
main_layout = (LinearLayout) findViewById(R.id.main_layout);
LayoutInflater infalter = LayoutInflater.from( this);
// 1, 等同于inflate(R.layout.button_layout, main_layout, true)
// infalter.inflate(R.layout.button_layout, main_layout);
// 2:因为在加载时,没有存在在一个布局中(加载完成之后才addView的),
// button_layout中Button设置layout_width和layout_height无效。
View buttonLayout = infalter.inflate(R.layout.button_layout, null);
main_layout.addView(buttonLayout);
// 3:将Button的layout_width和layout_height修改下试试,有效果。
// View buttonLayout = infalter.inflate(R.layout.button_layout, main_layout, false);
// main_layout.addView(buttonLayout);
}
}

注意:layout_width和layout_height是设置view在父布局中的大小
如果加载时没有父布局,不管你将Button的layout_width和layout_height的值修改成多少,都不会有任何效果的,因为这两个值现在已经完全失去了作用。
平时我们经常使用layout_width和layout_height来设置View的大小,并且一直都能正常工作,就好像这两个属性确实是用于设置View的大小的。而实际上则不然,它们其实是用于设置View在布局中的大小的,也就是说,首先View必须存在于一个布局中,之后如果将layout_width设置成match_parent表示让View的宽度填充满布局,如果设置成wrap_content表示让View的宽度刚好可以包含其内容,如果设置成具体的数值则View的宽度会变成相应的数值。这也是为什么这两个属性叫作layout_width和layout_height,而不是width和height。
再来看一下我们的button_layout.xml吧,很明显Button这个控件目前不存在于任何布局当中,所以layout_width和layout_height这两个属性理所当然没有任何作用。
那么怎样修改才能让按钮的大小改变呢?解决方法其实有很多种,最简单的方式就是在Button的外面再嵌套一层布局;或者加载时将其附到root布局中。

看到这里,也许有些朋友心中会有一个巨大的疑惑。不对呀!平时在Activity中指定布局文件的时候,最外层的那个布局是可以指定大小的呀,layout_width和layout_height都是有作用的。确实,这主要是因为,在setContentView()方法中,Android会自动在布局文件的最外层再嵌套一个FrameLayout,所以layout_width和layout_height属性才会有效果。

可以使用Demp view Hierarchy工具查看布局关系。
1.setContentView设置的布局外面,系统确实帮我们添加了一个FrameLayout,其id为android:id/content;其外层还有一个LinearLayout。
2.可以看到状态栏与该LinearLayout平级,id为android:id/statusBarBackground。


参考文章:
Android LayoutInflater原理分析-郭霖

猜你喜欢

转载自blog.csdn.net/maoshengdev/article/details/78228496