美文网首页
Android 优化之布局优化

Android 优化之布局优化

作者: tandeneck | 来源:发表于2020-05-08 12:06 被阅读0次

    布局优化的思想主要就是尽可能地减少布局文件的层级,减少系统绘制的工作量,这也符合扁平化设计。

    使用<include> 标签

    <include> 标签可以将一个指定的布局文件加载到另一个布局文件中,适合的场景是出现了公共页面模块,例如自定义的标题栏。

    <?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">
        
        <include layout="@layout/view_title"/>
        
        <TextView
            android:background="#fffff0"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </LinearLayout>
    

    通过在这里使用 <include> 标签,@layout/titlebar 指定了引入的布局为 view_title,达到了复用的目的,view_title 布局如下:

    <?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="44dp">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:padding="5dp"
            android:text="返回"
            android:textSize="15dp" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="5dp"
            android:text="标题"
            android:textSize="15dp" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:padding="5dp"
            android:text="完成"
            android:textSize="15dp" />
    
    </LinearLayout>
    

    <include> 标签只支持以 android:layout_* 开头的属性,例如 layout_width、layout_height,不过指定其他 layout_* 属性的话,android:layout_width 和 anroid:layout_height 必须存在。不过 android:id 这是一个例外,可以在 <include> 标签重新指定,而且以这个为准,无论被包含的布局文件的根元素是否指定了 id 属性。

    使用<merge> 标签

    <merge> 标签一般和 <include> 标签可以防止在引用布局文件时产生多余的布局嵌套,如下:

    <?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">
    
        <include layout="@layout/view_merge" />
    
    </LinearLayout>
    

    使用 <merge> 标签前:

    <?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">
    
        <TextView
            android:gravity="center"
            android:text="this is merge test."
            android:layout_width="match_parent"
            android:layout_height="50dp" />
    
    </LinearLayout>
    

    使用 <merge> 标签后:

    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="match_parent">
    
        <TextView
            android:gravity="center"
            android:text="this is merge test."
            android:layout_width="match_parent"
            android:layout_height="50dp" />
    
    </merge>
    

    这样就可以去掉了那多余的 LinearLayout,同理,当继承了 RelativeLayout 等ViewGroup 自定义 View 时,如果 xml 顶级标签和继承的 ViewGroup 效果相同时,应该把 xml 的顶级标签替换为 <include> 标签。例如:

    public class MergeView extends LinearLayout {
        public MergeView(Context context) {
            this(context, null);
        }
    
        public MergeView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public MergeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            LayoutInflater.from(context).inflate(R.layout.view_merge, this, true);
        }
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android">
    
        <TextView
            android:gravity="center"
            android:text="this is merge test."
            android:layout_width="match_parent"
            android:layout_height="50dp" />
    
    </merge>
    

    MergeView 通过 LayoutInflater 加载 view_merge 布局,而 view_merge 布局使用 <merge> 标签,达到了减少嵌套层级的目的。

    使用<ViewStub> 标签

    <ViewStub> 继承了 View,非常轻量且宽/高都是 0 ,ViewStub 的意义在于按需加载布局文件,在日常开发中,有很多布局文件在正常情况下不会显示,有些人可能会把不显示的布局设置为 GONE 或者 INVISIBLE,但这样设置它们还是加载在布局当中的,每个元素还时拥有着自己的宽、高、背景等等属性。如果使用 ViewStub 的话可以做到在使用的时候再加载,提高了程序初始化的性能。比如下面的一个示例是网络出现错误时才会加载的页面:

    public class NetworkErrorActivity extends AppCompatActivity {
    
        private TextView mContextTv;
        private TextView mErrorTv;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_network_error);
            mContextTv = findViewById(R.id.content_tv);
            mContextTv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mErrorTv == null) {
                        mErrorTv = (TextView) ((ViewStub) findViewById(R.id.error_stub)).inflate();
                    }
                }
            });
        }
    }
    

    布局activity_network_error:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".layoutoptim.NetworkErrorActivity">
    
        <TextView
            android:id="@+id/content_tv"
            android:text="正常显示页面"
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <ViewStub
            android:id="@+id/error_stub"
            android:inflatedId="@+id/error_tv"
            android:layout="@layout/view_network_error"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </FrameLayout>
    

    布局 error_stub:

    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:background="@android:color/white"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="网络出错了">
    
    </TextView>
    

    在 NetworkErrorActivity 的 mContextTv 的点击事件里去加载了 ViewStub 布局,加载完成后 ViewStub 就会被它内部的布局替换掉,这个时候 ViewStub 就不再是布局中的一部分了,这也导致了 inflate() 方法只能被调用一次,再次调用则会抛出异常,所以要做好相应的处理。ViewStub 中的 id 即 ViewStub 的 id,inflatedId 则是要被加载布局的根元素的 id。如果在被加载布局的根元素内部指定了 id 的同时指定 inflatedId ,以 inflatedId 为主。遗憾的是,ViewStub 目前所加载的布局不支持 <merge> 标签,这有可能导致加载出来的布局出现多余的嵌套结构。

    Double Taxation

    通常情况下,系统会在一次遍历中快速执行测量或布局阶段。但在一些情况比较复杂的布局中,在最终放置元素之前,系统可能必须对层次结构中需要多次遍历才能完成最终的效果。必须执行不止一次 “测量和布局”的情况称为 Double Taxation。例如,当使用 RelativeLayout 时,系统会执行以下操作:

    1. 执行一次“测量和布局”遍历,在此过程中,框架会根据每个子对象的请求计算对象的位置和大小。
    2. 结合数据和对象的权重确定关联视图的恰当位置。
    3. 执行第二次遍历,以最终确定对象的位置。
    4. 进入渲染过程的下一阶段。

    使用 ConstraintLayout 布局

    ConstraintLayout 可以很好地解决复杂页面的嵌套问题。

    相关文章

      网友评论

          本文标题:Android 优化之布局优化

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