我们在日常开发中,我们可能会遇到有很多相似的布局,如果每一个XML文件都写一次,不说麻烦,代码也显得冗余,而且可读性也很差.这时候就需要include 了,本编文章将会介绍include、merge和ViewStub标签的用法供大家学习和参考。
include标签
include标签常用于将布局中的公共部分提取出来供其他layout共用,以实现布局模块化,也是平常我们设计布局时用的最多的
include 官方文档
1.2、Activity的XML布局文件调用include标签:
1.3、Activity中调用include标签layout中的子View:
private void initView() {
//如果include布局根容器和include标签中的id设置的是不同的值,这里获取的mToolbar值将为null
Toolbar mToolbar = (Toolbar) findViewById(R.id.tb_toolbar); setSupportActionBar(mToolbar);
//普通include标签用法,直接拿子View属性实现
TextView textView = (TextView) findViewById(R.id.textView); textView.setText("不加ID实现的include标签");
//多个include标签用法,添加ID,findViewByID找到layout,再找子控件
View view_include = findViewById(R.id.include_text1); TextView view_include_textView = (TextView) view_include.findViewById(R.id.textView); view_include_textView.setText("加了ID实现的include标签");
//多个include标签用法,添加ID,findViewByID找到layout,再找子控件
View view_include_Relative = findViewById(R.id.include_text2); TextView view_textView_relative = (TextView) view_include_Relative.findViewById(R.id.textView); view_textView_relative.setText("加了ID实现的include标签(RelaviteLayout)"); }
==include使用注意==
- 一个xml布局文件有多个include标签需要设置ID,才能找到相应子View的控件,否则只能找到第一个include的layout布局,以及该布局的控件
- include标签如果使用layout_xx属性,会覆盖被include的xml文件根节点对应的layout_xx属性,建议在include标签调用的布局设置好宽高位置,防止不必要的bug
- include 添加id,会覆盖被include的xml文件根节点ID,这里建议include和被include覆盖的xml文件根节点设置同名的ID,不然有可能会报空指针异常
- 如果要在include标签下使用RelativeLayout,如layout_margin等其他属性,记得要同时设置layout_width和layout_height,不然其它属性会没反应
merge 标签
merge标签主要用于辅助include标签,在使用include后可能导致布局嵌套过多,多余的layout节点或导致解析变慢(可通过hierarchy viewer工具查看布局的嵌套情况)
官方文档说明:merge用于消除视图层次结构中的冗余视图,例如根布局是Linearlayout,那么我们又include一个LinerLayout布局就没意义了,反而会减慢UI加载速度
merge 官方文档
-
merge标签常用场景:
1.根布局是FrameLayout且不需要设置background或padding等属性,可以用merge代替,因为Activity的ContentView父元素就是FrameLayout,所以可以用merge消除只剩一个。
2.某布局作为子布局被其他布局include时,使用merge当作该布局的顶节点,这样在被引入时顶结点会自动被忽略,而将其子节点全部合并到主布局中。
3.自定义View如果继承LinearLayout(ViewGroup),建议让自定义View的布局文件根布局设置成merge,这样能少一层结点。
merge标签使用:
在XML布局文件的根布局如RelativeLayout直接改成merge即可
merge使用注意
1.因为merge标签并不是View,所以在通过LayoutInflate.inflate()方法渲染的时候,第二个参数必须指定一个父容器,且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点.
2.因为merge不是View,所以对merge标签设置的所有属性都是无效的.
3.注意如果include的layout用了merge,调用include的根布局也使用了merge标签,那么就失去布局的属性了
4.merge标签必须使用在根布局
5.ViewStub标签中的layout布局不能使用merge标签
ViewStub 标签
我们在做安卓项目的时候,经常会有一个使用场景:需要在运行时根据数据动态决定显示或隐藏某个View和布局。
上述场景,我们通常的解决方案就是:就是把可能用到的View先写在布局里,再初始化其可见性都设为View.GONE,然后在代码中根据数据动态的更改它的可见性。
虽然这样的实现,逻辑简单而且控制起来比较灵活;但是也存在一定的缺点耗费资源。
ViewStub 标签最大的优点是当你需要时才会加载,使用它并不会影响UI初始化时的性能.各种不常用的布局像进度条、显示错误消息等可以使用ViewStub标签,以减少内存使用量,加快渲染速度.ViewStub是一个不可见的,实际上是把宽高设置为0的View.效果有点类似普通的view.setVisible(),但性能体验提高不少
第一次初始化时,初始化的是ViewStub View,当我们调用inflate()或setVisibility()后会被remove掉,然后在将其中的layout加到当前view hierarchy中。
先来看看布局,一个是主布局,里面只定义二个ViewStub,一个用来控制TextView一个用来控制ImageView,另外就是一个是为显示文字的做的TextView布局,一个是为ImageView而做的布局:
为TextView的布局:
为ImageView的布局:
下面来看代码,决定来显示哪一个,只需要找到相应的ViewStub然后调用其infalte()就可以获得相应想要的布局:
public class ViewStubDemoActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewstub_demo_activity);
if ((((int) (Math.random() * 100)) & 0x01) == 0) {
// to show text
// all you have to do is inflate the ViewStub for textview
ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_text);
stub.inflate();
TextView text = (TextView) findViewById(R.id.viewstub_demo_textview);
text.setText("The tree of liberty must be refreshed from time to time" +
" with the blood of patroits and tyrants! Freedom is nothing but " +
"a chance to be better!");
} else {
// to show image
// all you have to do is inflate the ViewStub for imageview
ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_image);
stub.inflate();
ImageView image = (ImageView) findViewById(R.id.viewstub_demo_imageview);
image.setImageResource(R.drawable.happy_running_dog);
}
}
}
ViewStub标签使用注意
- ViewStub标签不支持merge标签
- ViewStub的inflate只能被调用一次,第二次调用会抛出异常,setVisibility可以被调用多次,但不建议这么做(ViewStub 调用过后,可能被GC掉,再调用setVisibility()会报异常)
- 为ViewStub赋值的android:layout_XX属性会替换待加载布局文件的根节点对应的属性
扩展:
Space组件
在ConstraintLayout出来前,我们写布局都会使用到大量的margin或padding,但是这种方式可读性会很差,加一个布局嵌套又会损耗性能
鉴于这种情况,我们可以使用space,使用方式和View一样,不过主要用来占位置,不会有任何显示效果