include、merge、ViewStub 标签使用

来源:互联网 发布:java jdk7.0 64位下载 编辑:程序博客网 时间:2024/06/13 03:47

include、merge、ViewStub 标签使用

include标签的使用

将需要重用的布局写在一个单独的xml文件中,再使用include标签复用到其他布局中
例如,下面是一个标题栏的布局文件:
titlebar.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="@dimen/base_action_bar_height"    android:background="@color/topic_green" >    <ImageView        android:id="@+id/tv_setting_title"        android:layout_width="100dip"        android:layout_height="fill_parent"        android:layout_alignParentTop="true"        android:layout_centerHorizontal="true"        android:gravity="center"        android:src="@drawable/logo_for_temp_white" />    <TextView        android:id="@+id/tv_back"        android:layout_width="wrap_content"        android:layout_height="fill_parent"        android:layout_alignParentTop="true"        android:layout_toRightOf="@+id/img_back"        android:drawableLeft="@drawable/avoscloud_feedback_thread_actionbar_back"        android:gravity="left|center_vertical"        android:text="返回"        android:textColor="@color/white"        android:textSize="@dimen/text_size_large" /></RelativeLayout>

如此就可以将这个标题栏直接复用到其他布局中了:
main_activity.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/ll_aty_choose_role"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:background="@drawable/background_for_temp"    android:orientation="vertical" >    <include layout="@layout/titlebar"/>    ...    </LinearLayout >

如此,titlebar的内容就可以嵌入到include标签所在的位置了。
需要注意的地方:
我们可在include标签中更改一些属性的值,比如重新设置id,改变布局属性(即android:layout_*属性)等,如下代码所示:

<include android:id=”@+id/news_titleandroid:layout_width=”match_parent”         android:layout_height=”match_parent”         layout="@layout/titlebar"/>
  • 若include标签中重新指定id,那么其中的控件就不可当成主xml(包含include标签的xml)中的控件来直接获得了,必须先获得include对应的xml文件(就是titlebar.xml),再通过布局文件的findViewById方法来获得其中控件。 当然,若原布局设置了id属性,会被覆盖掉。
  • 当需要在include标签中改变布局属性时,为了让其他属性生效,就必须重写android:layout_height和android:layout_width属性,否则任何针对layout调整都是无效的。
  • include有一个缺点就是可能会产生多余的层级,比如,被复用布局是一个垂直的LinearLayout布局,当以include标签插入到另一个垂直的LinearLayout布局中时,结果就是一个垂直的LinearLayout里包含一个垂直的LinearLayout,这个嵌套的布局并没有实际意义,只会让UI性能变差。这时就可以使用merge标签。

merge标签的使用

merge标签可以自动消除当一个布局插入到另一个布局时产生的多余的View Group,也可用于替换FrameLayout。用法就是直接使用merge标签标签作为复用布局的根节点,如下所示:

user.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">    <Button        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="姓名"/>    <Button        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="年龄"/></merge>

再使用include标签复用到其他布局中:

main_activity.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/ll_aty_choose_role"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:orientation="vertical" >    <include layout="@layout/user"/>    ...    </LinearLayout >

这时,系统会自动忽略merge标签,直接把两个Button替换到include标签的位置。

也就是说,include和merge是配合使用的。

需要注意的地方:
* merge标签只能作为复用布局的root元素来使用。
* 使用它来inflate一个布局时,必须指定一个ViewGroup实例作为其父元素并且设置attachToRoot属性为true(参考 inflate(int, android.view.ViewGroup, boolean) 方法的说明 )。

ViewStub

什么时候使用ViewStub?为什么使用ViewStub?

当我们需要根据某个条件控制某个View的显示或者隐藏的时候,通常是把可能用到的View都写在布局上,然后设置可见性为View.GONE或View.InVisible ,之后在代码中根据条件动态控制可见性。虽然操作简单,但是耗费资源,因为即便该view不可见,仍会被父窗体绘制,仍会创建对象,仍会被实例化,仍会被设置属性

android.view.ViewStub,是一个大小为0 ,默认不可见的控件,只有给他设置成了View.Visible或调用了它的inflate()之后才会填充布局资源,也就是说占用资源少。所以,推荐使用viewStub

ViewStub基本介绍

官方文档地址:https://developer.android.com/reference/android/view/ViewStub.html

ViewStub 继承自View。

官方文档原文:A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime. When a ViewStub is made visible, or when inflate() is invoked, the layout resource is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views. Therefore, the ViewStub exists in the view hierarchy until setVisibility(int) or inflate() is invoked. The inflated View is added to the ViewStub’s parent with the ViewStub’s layout parameters. Similarly, you can define/override the inflate View’s id by using the ViewStub’s inflatedId property. For instance:

我的翻译:
ViewStub 是一个不可见的,大小为0的视图,可以在运行过程中延时加载布局资源。当ViewStub被设置成可见,或者它的inflate() 方法被调用的时候,布局资源才会被填充,然后ViewStub本身就会被填充起来的布局资源替换掉。也就是说 ViewStub 被设置成可见或者它的inflate() 方法被调用之后,在视图树中就不存在了。被填充的布局在替换ViewStub的时候会使用ViewStub的布局参数(LayoutParameters),比如 width ,height等。此外,你也可以通过ViewStub的inflateId 属性定义或者重写 被填充布局资源的id。

 <ViewStub android:id="@+id/stub"               android:inflatedId="@+id/subTree"               android:layout="@layout/mySubTree"               android:layout_width="120dip"               android:layout_height="40dip" />

官方文档原文:The ViewStub thus defined can be found using the id “stub.” After inflation of the layout resource “mySubTree,” the ViewStub is removed from its parent. The View created by inflating the layout resource “mySubTree” can be found using the id “subTree,” specified by the inflatedId property. The inflated View is finally assigned a width of 120dip and a height of 40dip. The preferred way to perform the inflation of the layout resource is the following:
我的翻译:
在上面的示例代码中,可以通过id 获取到ViewStub,当layout属性引用的布局资源 mySubTree 被填充之后,ViewStub就会从它的父窗体中移除,取而代之的就是mySubTree。通过inflatedId 属性的值可以获取到mySubTree。mySubTree 在替代ViewSub的时候会使用ViewStub的layoutParametes, 也就是说mySubTree 的宽高会被定义成 120dp 、40dp。 推荐用如下方式去实现mySubTree布局资源的填充:

ViewStub stub = (ViewStub) findViewById(R.id.stub);View inflated = stub.inflate();

When inflate() is invoked, the ViewStub is replaced by the inflated View and the inflated View is returned. This lets applications get a reference to the inflated View without executing an extra findViewById().

我的翻译:
当ViewStub 的inflate() 方法被调用之后,ViewStub就会被填充起来的布局替换掉,并返回填充起来的View。这样,当我们想使用被填充起来的View时就不再需要调用findViewById () 方法。(实际使用的时候,如果我们需要操作被填充布局里面的数据时用inflate(),否则可以直接使用setVisibility)

示例代码

activity_viewstub_test02.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:orientation="vertical">    <LinearLayout android:layout_width="match_parent"                  android:layout_height="wrap_content">        <Button            android:id="@+id/btn_vs_showView"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:text="显示ViewStub"/>        <Button            android:id="@+id/btn_vs_changeHint"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:text="更改ViewStub"/>        <Button            android:id="@+id/btn_vs_hideView"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_alignParentRight="true"            android:layout_weight="1"            android:text="隐藏ViewStub"/>    </LinearLayout>    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        android:text="正在加载。。。"/>    <!--ViewStub 展示或者隐藏内容-->    <ViewStub        android:id="@+id/viewstub_test"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:inflatedId="@+id/iv_VsContent"        android:layout="@layout/iv_vs_content"/></RelativeLayout>

ViewStubTestActivitiy.java

/** * Created by CnPeng on 2017/1/11\. ViewStub 的使用示例 */public class ViewStubTestActivitiy extends AppCompatActivity implements View.OnClickListener {    private ViewStub viewStub;    private TextView hintText;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_viewstub_test02);        init();    }    /**     * 初始化     */    private void init() {        viewStub = (ViewStub) findViewById(R.id.viewstub_test);        Button btn_show = (Button) findViewById(R.id.btn_vs_showView);        Button btn_hide = (Button) findViewById(R.id.btn_vs_hideView);        Button btn_change = (Button) findViewById(R.id.btn_vs_changeHint);        btn_show.setOnClickListener(this);        btn_hide.setOnClickListener(this);        btn_change.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btn_vs_showView:                //inflate 方法只能被调用一次,因为调用后viewStub对象就被移除了视图树;                // 所以,如果此时再次点击显示按钮,就会崩溃,错误信息:ViewStub must have a non-null ViewGroup viewParent;                // 所以使用try catch ,当此处发现exception 的时候,在catch中使用setVisibility()重新显示                try {                    View iv_vsContent = viewStub.inflate();     //inflate 方法只能被调用一次,                    hintText = (TextView) iv_vsContent.findViewById(R.id.tv_vsContent);                    //                    hintText.setText("没有相关数据,请刷新");                } catch (Exception e) {                    viewStub.setVisibility(View.VISIBLE);                } finally {                    hintText.setText("没有相关数据,请刷新");                }                break;            case R.id.btn_vs_hideView:  //如果显示                viewStub.setVisibility(View.INVISIBLE);                break;            case R.id.btn_vs_changeHint:                if (hintText!=null) {                     hintText.setText("网络异常,无法刷新,请检查网络");                }                break;        }    }}

iv_vs_content.xml –要通过ViewStub展示出来的内容

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <ImageView        xmlns:android="http://schemas.android.com/apk/res/android"        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="1"        android:src="@mipmap/ic_launcher"/>    <TextView        android:id="@+id/tv_vsContent"        android:layout_width="match_parent"        android:layout_height="@dimen/dp30"        android:gravity="center"        android:text="eeee"/></LinearLayout>

使用总结

总结:
1)ViewStub 引用布局时使用layout 属性,取值@layout/xxxxx
2)InflateId 表示给被引用的布局的id。也就是被控制显示或隐藏的布局的id.
3)如果不写InflateId ,如果需要的话也可以直接在被引用的布局中给出id属性
4)inflate() 方法只能被调用一次,如果再次调用会报异常信息 ViewStub must have a non-null ViewGroup viewParent

这是因为,当ViewStub 调用inflate() 将其引用的 布局/view 展示出来之后,ViewStub本身就会从视图树中被移除,此时viewStub 就获取不到他的 父布局, 而 inflate() 方法中,上来就需要获取它的父布局,然后根据父布局是否为空再去执行具体的填充逻辑,如果为空就报上面的错,所以,inflate() 之后如果还想再次显示ViewStub 引用的布局/view 就需要 在调用inflate() 的时候try catch,当 catch 到异常的时候,调用setVisibility()设置viewStub 的View.Visible即可。ViewStub类中Inflate() 的具体逻辑如下:

public View inflate() {    final ViewParent viewParent = getParent();    if (viewParent != null && viewParent instanceof ViewGroup) {        if (mLayoutResource != 0) {            final ViewGroup parent = (ViewGroup) viewParent;            final LayoutInflater factory;            if (mInflater != null) {                factory = mInflater;            } else {                factory = LayoutInflater.from(mContext);            }            final View view = factory.inflate(mLayoutResource, parent,                    false);            if (mInflatedId != NO_ID) {                view.setId(mInflatedId);            }            final int index = parent.indexOfChild(this);            parent.removeViewInLayout(this);            final ViewGroup.LayoutParams layoutParams = getLayoutParams();            if (layoutParams != null) {                parent.addView(view, index, layoutParams);            } else {                parent.addView(view, index);            }            mInflatedViewRef = new WeakReference<View>(view);            if (mInflateListener != null) {                mInflateListener.onInflate(this, view);            }            return view;        } else {            throw new IllegalArgumentException("ViewStub must have a valid layoutResource");        }    } else {        throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");    }}  

5) ViewStub的setVisibility()中也调用了inflate(),但是为什么多次调用setVisibility()不会导致崩溃呢?

ViewStub 的setVisibility() 方法中,会先判断 WeakReference 类型的成员变量 mInflatedViewRef 是否为空。第一次调用setVisibility()的时候,mInflatedViewRef并没有初始化,也就是说是null,那么这时候就会走inflate(),在inflate() 方法中给被填充起来的布局/view创建一个WeakReference弱引用,并赋值给mInflatedViewRef,从而完成mInflatedViewRef的初始化。当第二次走setVisibility() 的时候,mInflatedViewRef已经不再是null,就会调用 WeakReference 的父类Reference 中的get() 方法获取该引用指向的实体对象,也就是说通过get() 拿到 被填充的view对象,然后再走View类的setVisibility()。ViewStub类中的setVisibility()具体实现如下:

@Override@android.view.RemotableViewMethodpublic void setVisibility(int visibility) {    if (mInflatedViewRef != null) {        View view = mInflatedViewRef.get();        if (view != null) {            view.setVisibility(visibility);        } else {            throw new IllegalStateException("setVisibility called on un-referenced view");        }    } else {        super.setVisibility(visibility);        if (visibility == VISIBLE || visibility == INVISIBLE) {            inflate();        }    }}

6) 根据上面 第4点和第5点可以得出如下结论:

(1) ViewStub 的inflate() 只能被调用一次!

(2) 如果想控制/修改 被填充布局中的内容并重复显示被填充的view,就用try 将viewStub.inflate() 以及修改内容的代码包裹起来,并在catch 中setVisibility.

7) 在xml 中定义ViewStub 节点时,内部不能包含其他节点,也就是说,ViewStub 是一个自闭合节点,如果一个布局/view如果想通过ViewStub显示,只能定义在单独的xml 文件中。

引用:
Android include与merge标签使用详解
ViewStub–使用介绍

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 猫长跳蚤怎么办 幼猫有跳蚤怎么办 二个月小猫拉稀怎么办 小奶猫有跳蚤怎么办 蛇颈龟病了怎么办 巴西龟冬天不吃怎么办 巴西龟不吃肉怎么办 小巴西龟吃东西怎么办 乌龟流鼻涕怎么办 巴西红耳龟死了怎么办 我想放生巴西龟怎么办 红耳龟不吃东西怎么办 乌龟不爱吃东西怎么办 黄头龟有点水肿怎么办 黄头龟水肿怎么办 黄头侧颈龟水肿怎么办? 黄头龟爪子肿怎么办 黄头龟四肢水肿怎么办 猪鼻龟肿了怎么办 全身肿怎么办 黄头龟腐甲水肿怎么办 黄头侧颈龟脱皮怎么办 黄头侧颈龟掉皮怎么办 黄头龟腹甲烂了怎么办 黄头龟烂甲了怎么办 黄头龟龟壳烂了怎么办 乌龟腐甲怎么办 龟板腐甲怎么办 乌龟不冬眠怎么办 龟水肿怎么办 小鳄龟胆子小怎么办 鳄龟养大了你们怎么办 想要大鳄龟怎么办 遇到狗咬人空手怎么办 大鳄龟不钓鱼怎么办 成年狗狗咬人怎么办 两个月狗狗咬人怎么办 三个月狗狗咬人怎么办 鹦鹉老咬人怎么办 遇到狗咬人怎么办ppt 狗咬人不松口怎么办