Android 布局优化全解析:include、merge、ViewStub 实战与最佳实践(图文版)

一、为什么要做布局优化?

Android 布局性能直接决定了应用的首屏加载速度与流畅度。

复杂的布局层级会导致:

  • measure / layout / draw 次数增加

  • 界面绘制变慢、掉帧卡顿

  • View 创建与内存占用上升

所以我们要通过:

✅ include

✅ merge

✅ ViewStub

实现布局结构优化。


二、三大优化手段关系图

下图展示了三者的区别和作用:

复制代码
┌───────────────────────────────┐
│         布局优化手段          │
├──────────┬───────────┬──────────┤
│ include  │ merge     │ ViewStub │
├──────────┼───────────┼──────────┤
│ 重用布局 │ 减少层级  │ 延迟加载 │
│ Header、Footer │ RecyclerView Item │ 空页、错误页 │
└──────────┴───────────┴──────────┘

📈 性能影响从小到大:

include < merge < ViewStub(延迟加载最优)


三、<include> ------ 复用布局的利器

🧱 典型结构图

复制代码
activity_main.xml
│
├── include -> header_layout.xml
│       ├── ImageView (返回键)
│       └── TextView (标题)
│
└── 内容区域

✅ 使用示例

XML 复制代码
<!-- header_layout.xml -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:background="@color/black">

    <ImageView
        android:id="@+id/iv_back"
        android:src="@drawable/ic_back"
        android:layout_width="24dp"
        android:layout_height="24dp"/>

    <TextView
        android:id="@+id/tv_title"
        android:text="标题"
        android:textColor="@color/white"
        android:textSize="18sp"
        android:layout_marginStart="16dp"/>
</LinearLayout>
XML 复制代码
<!-- activity_main.xml -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/header_layout"
             android:id="@+id/include_header"/>

    <TextView
        android:text="内容区域"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

⚠️ 注意事项

  1. includeid 仅作用于引用处,子布局 id 不会被替代。

  2. 外层可重新定义 layout_widthlayout_height


四、<merge> ------ 减少多余布局层级

🧩 对比结构图

错误示例:

复制代码
LinearLayout (外层)
└── include -> LinearLayout (item_user.xml)
        ├── ImageView
        └── TextView

正确示例:

复制代码
LinearLayout (外层)
├── ImageView
└── TextView

✅ 实现示例

XML 复制代码
<!-- item_user.xml -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <ImageView
        android:id="@+id/avatar"
        android:layout_width="40dp"
        android:layout_height="40dp"/>
    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"/>
</merge>
XML 复制代码
<!-- 使用 -->
<LinearLayout
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <include layout="@layout/item_user"/>
</LinearLayout>

⚠️ 注意事项

  • <merge> 不能独立使用(必须 include 或动态 inflate)。

  • 无法直接访问根布局(它被合并进父布局)。


五、<ViewStub> ------ 按需延迟加载隐藏布局

🕓 原理示意图

复制代码
初始状态:
FrameLayout
├── MainContent
└── ViewStub (占位,不占资源)

当触发 inflate():
FrameLayout
├── MainContent
└── ErrorLayout (由 ViewStub 替换生成)

✅ 示例代码

XML 复制代码
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/layout_content" />

    <ViewStub
        android:id="@+id/view_stub_error"
        android:inflatedId="@+id/layout_error"
        android:layout="@layout/layout_error"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>
Kotlin 复制代码
val viewStub = findViewById<ViewStub>(R.id.view_stub_error)
val errorView = viewStub.inflate()
errorView.findViewById<TextView>(R.id.tv_error).text = "加载失败,请重试"

⚠️ 注意事项

  • inflate() 只能调用一次。

  • 绑定 ViewBindingDataBinding 时需在 inflate() 之后执行。

  • 适合错误页、空数据页、引导页等不常显示的布局


六、三者对比表

特性 include merge ViewStub
作用 复用布局 减少嵌套 延迟加载
是否创建 View 合并入父布局 延迟加载时创建
性能优化级别 最高
常见场景 Header/Footer RecyclerView item 空页、错误页、引导页
注意事项 属性覆盖 无根节点 inflate 一次性

七、性能可视化示意图

下图表示布局层级优化前后的渲染层次:

复制代码
未优化:
FrameLayout
 └── LinearLayout
      └── LinearLayout
           └── TextView

优化后 (merge):
FrameLayout
 └── LinearLayout
      └── TextView

结果:

  • measure/layout 次数减少约 20--30%

  • 渲染时间缩短,首屏更快


八、实战:错误页延迟加载优化

XML 复制代码
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/layout_content" />

    <ViewStub
        android:id="@+id/stub_error"
        android:layout="@layout/layout_error"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</FrameLayout>
Kotlin 复制代码
class MainActivity : AppCompatActivity() {
    private var errorView: View? = null

    fun showError() {
        if (errorView == null) {
            errorView = findViewById<ViewStub>(R.id.stub_error).inflate()
        }
        errorView?.visibility = View.VISIBLE
    }

    fun hideError() {
        errorView?.visibility = View.GONE
    }
}

九、总结:何时用哪一个?

目的 推荐方式 原因
复用相同布局 include 简单可靠
避免重复父布局 merge 减少层级
延迟加载不常用布局 ViewStub 节省内存与渲染时间

🔚 写在最后

布局优化不是炫技,而是「让 UI 加载更快」的基础功。

掌握 includemergeViewStub 的组合拳,

你可以让应用启动速度提升、UI 更流畅。

相关推荐
金仓拾光集3 小时前
筑牢风控生命线:金仓数据库替代MongoDB,重构证券融资融券业务的数据基石
数据库·mongodb·信创·1024程序员节·kingbasees·国产化替代
奎歪歪3 小时前
UniApp缓存系统详解
缓存·uni-app·1024程序员节
情深不寿3173 小时前
MySQL————mysql connect
1024程序员节
AI浩3 小时前
MFFCI-YOLOv8:一种基于多尺度特征融合与上下文信息的轻量级遥感目标检测网络
1024程序员节
mit6.8243 小时前
[OP-Agent] 可扩展架构 | 插件管理器plugins.go
go·1024程序员节
cai_huaer4 小时前
BugKu Web渗透之 文件包含2
web安全·1024程序员节
蓝天居士4 小时前
PY32F040单片机介绍(1)
单片机·国产mcu·1024程序员节
ʚ希希ɞ ྀ4 小时前
初学JVM---什么是JVM
1024程序员节
2401_888859714 小时前
STM32_bug总结-运行函数在SystemInit之后就卡死
1024程序员节