美文网首页
android 之布局优化

android 之布局优化

作者: 0246eafe46bd | 来源:发表于2021-12-12 15:32 被阅读0次

布局优化原因

如果布局嵌套过深,或者界面元素复杂等原因导致布局渲染性能不佳,可能会导致应用卡顿

UI层级查看工具

Android Studio -> Tools -> Layout Inspector


image-20211207214505023.png

优化 UI 层级方法

  1. 使用 include 标签复用布局
  2. 使用 merge 标签减少布局层级
  3. 使用 ViewStub 标签延迟 inflater 非必须布局
  4. 尽量使用 ConstraintLayout 使视图层次结构扁平化

include

include 是为了解决重复定义相同布局的问题,将多次使用的布局内容独立成一个 xml 文件,然后在需要使用的地方通过 include 标签引入即可

例如:将标题栏布局独立成一个 xml 文件,再通过 include 标签引入

title.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/title_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/title_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Back"
        android:textAllCaps="false"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/title_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Title Text"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/title_edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Edit"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <include
        android:id="@+id/common_title_root"
        layout="@layout/title" />
</androidx.constraintlayout.widget.ConstraintLayout>

注意点

当 include 标签和被引入的根布局,同时设置了 id 属性的时候,以 include 标签的 id 为准。如上面的例子,如果使用 findViewById(R.id.title_root) 将返回 null

merge

使用工具 Layout Inspector 查看上面例子的 UI 层级结构,会发现,多了一层 ConstraintLayout,如下图:


image-20211207221843517.png

这是 title.xml 文件的根布局,这一层布局嵌套是不必要的,merge 就用于解决这一层不必要的布局嵌套,将 title.xml 最外层的 ConstraintLayout 布局换用成 merge 标签,表示当要 include title.xml 这个布局时,会将 merge 标签内包含的内容直接填充到 include 的位置,因此不会额外添加布局结构。修改后的 title.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <Button
        android:id="@+id/title_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Back"
        android:textAllCaps="false"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/title_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Title Text"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/title_edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Edit"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</merge>

使用工具 Layout Inspector 查看修改后的 UI 层级结构,会发现少了一层 ConstraintLayout


image-20211207222727153.png

ViewStub

是延迟加载的一种方式,它通过 android:inflatedId 属性指向一个布局,当 ViewStub 被设置为可见或者 ViewStub.inflate() 被调用的时候,inflatedId 属性指向的布局就会被实例化为 View, 并且按照 ViewStub 的布局属性添加到 ViewStub 的父 View 中

修改上面例子中的 activity_main.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/show_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="show the title view"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ViewStub
        android:id="@+id/stub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inflatedId="@+id/title_root"
        android:layout="@layout/title"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.java 改为点击按钮才加载 title.xml 布局

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        show_title.setOnClickListener {
            var titleRoot = stub.inflate()
        }
    }
}

注意点

ViewStub 的 android:layout 不能指向一个 merge 标签包裹的布局文件,不然会报以下错误

Process: com.example.androidstudy, PID: 7999
android.view.InflateException: Binary XML file line #3: <merge /> can be used only with a valid ViewGroup root and attachToRoot=true
Caused by: android.view.InflateException: <merge /> can be used only with a valid ViewGroup root and attachToRoot=true

因此要将 title.xml 文件的 merge 改为 ConstraintLayout ,或者其他 ViewGroup

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/title_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!--其他内容不改动-->
    
</androidx.constraintlayout.widget.ConstraintLayout>

执行代码,先不点击按钮执行 stub.inflate() ,使用工具 Layout Inspector 查看 UI 层级结构,如下:


image-20211207230013871.png

点击按钮,执行 ViewStub .inflate() 方法后,使用工具 Layout Inspector 查看 UI 层级结构,发现 stub 换成了 title.xml 文件中的布局


image-20211207230252032.png
ViewStub 的 inflate() 方法只能执行一次,如果多次执行,会报以下错误
Process: com.example.androidstudy, PID: 8545
    java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.ViewStub.inflate()' on a null object reference

相关文章

网友评论

      本文标题:android 之布局优化

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