美文网首页
布局优化include, viewstub, merge

布局优化include, viewstub, merge

作者: 黄海佳 | 来源:发表于2017-05-16 11:27 被阅读181次
1 include

视图引入,可以配合merge使用
重用布局

2.merge

<merge/>标签在UI的结构优化中起着非常重要的作用,它可以删减多余的层级,优化UI。<merge/>多用于替换FrameLayout或者当一个布局包含另一个时,<merge/>标签消除视图层次结构中多余的视图组。例如你的主布局文件是垂直布局,引入了一个垂直布局的include,这是如果include布局使用的LinearLayout就没意义了,使用的话反而减慢你的UI表现。这时可以使用<merge/>标签优化。
减少视图层级
步骤如下:
创建merge视图

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="123" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="123" />
</merge>

视图引入

<?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/activity_main"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent"
    tools:context="com.example.ticker.myapplication.MainActivity">
    <include layout="@layout/merge_layout"></include>
</LinearLayout>
3.ViewStub

当我们需要根据某个条件控制某个View的显示或者隐藏的时候,通常是把可能用到的View都写在布局上,然后设置可见性为View.GONE或View.InVisible ,之后在代码中根据条件动态控制可见性。虽然操作简单,但是耗费资源,因为即便该view不可见,仍会被父窗体绘制,仍会创建对象,仍会被实例化,仍会被设置属性。
引入一个外部布局,与上面不同的是,viewstub引入的布局默认不会扩张,即既不会占用显示也不会占用位置,从而在解析layout时节省cpu和内存。ViewStub是一个不可见的,大小为0的视图,只有给他设置成了View.Visible或调用了它的inflate()之后才会填充布局资源。可以在运行过程中延时加载布局资源,inflate 方法只能被调用一次,因为调用后viewStub对象就被移除了视图树;

注:ViewStub目前有个缺陷就是还不支持 <merge /> 标签。

使用步骤如下:
1、创建需要加载的布局

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">
    <Button
        android:layout_width="match_parent"
        android:text="button01"
        android:layout_height="wrap_content" />
    <Button
        android:layout_width="match_parent"
        android:text="button02"
        android:layout_height="wrap_content" />
</LinearLayout>

在需要引入的xml中引入

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.ticker.myapplication.MainActivity">

<Button
            android:id="@+id/btn_vs_showView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="显示ViewStub"/>
    <ViewStub
        android:id="@+id/viewstub"
        android:layout_width="match_parent"
        android:layout="@layout/viewstub_layout"
        android:layout_height="match_parent" />
</RelativeLayout>

3、使用

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewStub viewStub = (ViewStub) findViewById(R.id.viewstub);
        View view = viewStub.inflate();
        Button btn_show = (Button) findViewById(R.id.btn_vs_showView);
    }


      @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;
           
        }
    }
}
4、使用总结

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");
    }
}
  1. 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.RemotableViewMethod
public 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();
        }
    }
}

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

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

相关文章

  • Android-性能优化

    应用体验-布局优化 使用include布局、merge标签、ViewStub视图可以使用HierarchyView...

  • Android 性能优化

    布局优化 include 标签 比如导航栏merge 标签 减少布局的层级viewstub 继承view 本身不...

  • android应用性能优化

    1. UI布局的优化 使用include,merge,ViewStub标签优化布局 尽量不存在冗余嵌套及过于复杂的...

  • Android 知识

    Android 知识随笔 1.布局优化include merge viewstub 2视频播放流程 采集 —>处理...

  • Android 性能优化(UI渲染)

    注意事项: 布局优化;尽量使用include、merge、ViewStub标签,尽量不存在冗余嵌套及过于复杂布局(...

  • Android面试宝典 - 优化篇

    一、性能优化 1. 布局优化 尽量使用include,merge,ViewStub标签避免冗余嵌套以及过于复杂的布...

  • Android 性能优化

    布局优化 include 标签。 merge 标签。 ViewStub 视图。 减少视图绘制:1.尽量避免在列表布...

  • Android 性能优化

    布局优化 include 标签。 merge 标签。 ViewStub 视图。 减少视图绘制:1.尽量避免在列表布...

  • Android抽象布局——include、merge 、View

    Android抽象布局——include、merge 、ViewStub - CSDN博客

  • 布局优化include, viewstub, merge

    1 include 视图引入,可以配合merge使用重用布局 2.merge 标签在UI的结构优化...

网友评论

      本文标题:布局优化include, viewstub, merge

      本文链接:https://www.haomeiwen.com/subject/xytlxxtx.html