Drawer — 什么是抽屉导航,DrawerLayout 和应用中的侧边菜单

作者: IT Sectr 发布日期: 2026-02-23 阅读时间: 12 分钟

Drawer(Navigation Drawer)——一个交互式面板,从屏幕左侧(或右侧)边缘滑出并显示导航链接。在 Android 上通过 DrawerLayout 实现,在 iOS 上通过自定义解决方案(如 SideMenu)或 iPad 上的标准 UISplitViewController 实现。有关更多规范详情,请阅读 Material Design 3 文档

要点

  • Drawer——通过滑动或点击“汉堡”图标打开的侧边导航菜单
  • DrawerLayout——Android 中实现滑动菜单的标准容器
  • SideMenu——用于可自定义侧边菜单的热门 iOS 库
  • Modal Drawer——覆盖内容,用于聚焦导航
  • Standard Drawer——将内容向右推移,保留部分可见

什么是 Drawer?

Drawer(Navigation Drawer)——一种导航元素,以面板形式从屏幕左侧边缘通过滑动手势或点击“汉堡”图标(三条水平线)滑出。Drawer 包含导航链接、标题和可选元素:用户头像、通知计数器、分隔线。面板可以覆盖内容(Modal)或推移内容(Standard)。

Material Design 3(2026)将 Navigation Drawer 定义为适用于具有 6+ 个导航点的应用程序的组件。与 Bottom Navigation 不同,Drawer 不占用屏幕上的固定位置,仅在用户请求时显示。这释放了内容空间,但需要额外的操作才能打开。Google 建议将 Drawer 用于具有深层部分层次结构的应用程序。

UX Planet(2025)的研究表明:Drawer 适用于具有 5-15 个部分的应用程序,但导航时间比 Bottom Navigation 高 40%。用户会错过屏幕折叠线(滚动)下方的菜单项。因此,重要的部分应位于 Drawer 的顶部,而不太重要的部分(关于应用、设置)应位于底部,用分隔线隔开。

Drawer 类型:Modal 和 Standard

Modal Drawer——滑出覆盖内容,使背景变暗。用于屏幕宽度有限的移动设备。选择项目后,Drawer 自动关闭。Standard Drawer(Permanent)——一个固定面板,始终可见,推移内容。用于屏幕宽度 900 dp 以上的平板电脑和桌面设备。Material 3 还定义了 Rail Drawer——适用于平板电脑的紧凑版本,仅显示图标。

类型行为用途
Modal覆盖内容,使背景变暗手机,临时导航
Standard推移内容,固定平板电脑,桌面(900+ dp)
Rail带图标的紧凑侧面板平板电脑,自适应导航

Android 中的 DrawerLayout

DrawerLayout——Android 中实现滑动侧边菜单的标准 ViewGroup。DrawerLayout 包含两个子元素:第一个——主要内容(带有 NavHostFragment 的 FrameLayout),第二个——带有菜单的 NavigationView。DrawerLayout 处理滑动手势和通过 ActionBarDrawerToggle 进行的程序化打开/关闭。

kotlin
// 使用 DrawerLayout 和 NavigationView 的 Activity
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var drawerLayout: DrawerLayout

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        drawerLayout = binding.drawerLayout
        val navView = binding.navView
        val navController = findNavController(R.id.nav_host_fragment)

        // 将 Drawer 连接到 Navigation Component
        NavigationUI.setupActionBarWithNavController(
            this, navController, drawerLayout
        )
        NavigationUI.setupWithNavController(navView, navController)
    }

    override fun onSupportNavigateUp(): Boolean {
        return NavigationUI.navigateUp(
            findNavController(R.id.nav_host_fragment),
            drawerLayout
        )
    }
}

// XML: activity_main.xml
// <androidx.drawerlayout.widget.DrawerLayout
//   android:id="@+id/drawerLayout"
//   ...>
//   <!-- 主要内容 -->
//   <FrameLayout android:id="@+id/nav_host_fragment"
//     android:layout_width="match_parent"
//     android:layout_height="match_parent" />
//   <!-- Navigation Drawer -->
//   <com.google.android.material.navigation.NavigationView
//     android:id="@+id/navView"
//     android:layout_width="wrap_content"
//     android:layout_height="match_parent"
//     android:layout_gravity="start"
//     app:menu="@menu/nav_menu" />
// </androidx.drawerlayout.widget.DrawerLayout>

NavigationView——用于 Drawer 内容的 Material Design 组件。支持标题(headerLayout)、菜单项(menu)、副标题、复选框和标志。app:menu 属性指向包含导航点的 XML 菜单。NavigationUI 自动突出显示活动项并与 NavController 同步状态。

iOS 中的 Side Menu

iOS 没有内置的“Drawer”组件——Apple 不建议在 iPhone 上使用侧边菜单(HIG,2026)。然而,对于 iPad,使用带有 primary 和 secondary 面板的 UISplitViewController。对于 iPhone,侧边菜单通过第三方库(SideMenu、SwiftUI-side-menu)或带有内容偏移的自定义解决方案实现。在 SwiftUI 中,侧边菜单通过带有偏移动画的 ZStack 创建。

swift
import SwiftUI

struct DrawerView<Content: View, Menu: View>: View {
    let menuWidth: CGFloat = 280

    @Binding var isOpen: Bool
    @ViewBuilder let content: Content
    @ViewBuilder let menu: Menu

    var body: some View {
        ZStack(alignment: .leading) {
            content
                .offset(x: isOpen ? menuWidth : 0)
                .animation(.easeInOut, value: isOpen)

            if isOpen {
                menu
                    .frame(width: menuWidth)
                    .transition(.move(edge: .leading))

                Color.black.opacity(0.3)
                    .ignoresSafeArea()
                    .onTapGesture { isOpen = false }
            }
        }
    }
}

// 使用
struct ContentView: View {
    @State private var showDrawer = false

    var body: some View {
        DrawerView(isOpen: $showDrawer) {
            NavigationStack {
                HomeView()
                    .toolbar {
                        ToolbarItem(placement: .navigationBarLeading) {
                            Button(action: { showDrawer.toggle() }) {
                                Image(systemName: "line.horizontal.3")
                            }
                        }
                    }
            }
        } menu: {
            DrawerMenuView()
        }
    }
}

对于 iOS 的生产项目,经常使用 SideMenu 库(GitHub,8k+ 星标)。它提供现成的 UIViewController,支持手势、背景模糊、自定义宽度和动画。SideMenu 支持左侧和右侧菜单、多个层级以及通过 UINavigationController 与 UIKit 集成。替代方案——iPad 上的 UISplitViewController(系统级,无自定义动画)。

自定义 Drawer:标题、图标、区域

Drawer 允许广泛的自定义。在 Android 中,NavigationView 支持 headerLayout——放置在菜单项上方的自定义布局。其中放置了头像、用户名、电子邮件和封面。菜单项通过 <menu> 进行分组,包含副标题、分隔线和标志。在 iOS 中,SideMenu 的自定义包括设置背景颜色、模糊、宽度和圆角。

kotlin
// NavigationView 的自定义 header
val headerView = binding.navView.inflateHeaderView(R.layout.nav_header)
headerView.findViewById<ImageView>(R.id.iv_avatar)
    .setImageResource(R.drawable.avatar)
headerView.findViewById<TextView>(R.id.tv_name)
    .text = "John Doe"

// 配置菜单项
val menuItem = binding.navView.menu.findItem(R.id.nav_notifications)
menuItem.isChecked = true

// 添加徽章
val badge = binding.navView.getOrCreateBadge(R.id.nav_notifications)
badge.number = 15
badge.isVisible = true

Material Design 建议以下 Drawer 结构:标题(姓名、头像)、主要导航项(5-8)、分隔线、次要项(设置、帮助)、页脚(关于应用、版本)。在 Material 3 中,NavigationView 还支持通过 itemShape 和 itemBackground 自定义活动项的突出显示。

何时使用 Drawer:UX 建议

Drawer 对于具有 6+ 个部分、设置和帮助页面的应用程序有效。不适用于快速访问部分至关重要的应用程序——在这种情况下,Bottom Navigation 更好。Material Design 建议将 Drawer 与 Bottom Navigation 结合使用:主导航——Bottom(3-5 个标签),附加导航——Drawer(设置、帮助、关于应用)。

实现 Drawer 的主要错误:菜单过载(15+ 项未分组)、重要部分隐藏在折叠线下方、缺少图标(无图标的文本扫描速度慢 30%)、动画延迟(应为 ≤ 200 ms)。最佳实践:带有用户名的标题、每项都有图标、组之间的分隔线、活动部分突出显示、支持滑动手势打开。

  • 6+ 部分——Drawer 是合理的,更少——请使用 Bottom Navigation
  • 图标——每项必需,加速视觉搜索
  • 分组——主要和次要项之间的分隔线
  • 标题——用于个性化的头像、姓名和电子邮件(社交应用)
  • 滑动手势——打开必需,由“汉堡”按钮重复

常见问题解答

Drawer 与 Bottom Navigation 有何不同?

Drawer——通过手势打开的隐藏侧边菜单,适用于 6+ 个部分和深层层次结构。Bottom Navigation——始终可见的底部面板,适用于 3-5 个部分。Drawer 节省屏幕空间,但需要额外的操作才能打开,这会使导航时间增加 40%。

如何在 SwiftUI 中添加 Drawer?

SwiftUI 没有内置的 Drawer。使用 ZStack 配合内容偏移和动画,如上例所示。生产环境使用库:SideMenu(Swift Package)或围绕 UISplitViewController 的自定义封装。在 iPad 上使用 preferredDisplayMode 为 .oneBesideSecondary 的 UISplitViewController。

为什么 Apple 不建议在 iPhone 上使用 Drawer?

Apple HIG(2026)指出:侧边菜单隐藏内容并违反直接操作原则。iOS 建议使用 Tab Bar 进行导航。Drawer 在 iPhone 上仅用于设置和附加操作,不用于主导航。在 iPad 上,Drawer 被系统标准的 UISplitViewController 取代。

如何处理 Drawer 的滑动手势?

在 Android 中,DrawerLayout 自动处理从左侧边缘的滑动。在 iOS 中,自定义 Drawer 需要 UIPanGestureRecognizer 或 Edge Pan Gesture(SwiftUI——带有 DragGesture 的 .gesture)。重要提示:滑动可能与系统性的“返回”手势冲突——在 iOS 上,左侧边缘被系统手势占用。

Drawer 可以放在右侧吗?

可以。在 Android 中,为 NavigationView 设置 android:layout_gravity="end"。在 iOS 中,使用 edge: .trailing 创建菜单。右侧 Drawer 较少使用——用于附加选项、过滤器或聊天。主导航根据 Material Design 和用户期望保持在左侧。

总结

  • Drawer——6+ 个部分的侧边导航菜单,通过滑动或点击“汉堡”打开
  • DrawerLayout(Android)——通过 NavigationUI 集成了 Navigation Component 的标准 ViewGroup
  • SideMenu(iOS)——用于自定义 Drawer 的热门库,因为 Apple 不提供内置组件
  • Modal vs Standard——Modal 覆盖内容(手机),Standard 推移内容(平板电脑 900+ dp)
  • 自定义——带头像的标题、项分组、徽章、活动部分突出显示
  • UX——Drawer 比 Bottom Navigation 慢 40%,但对于具有深层层次结构的应用程序是必需的
  • Material 3——NavigationView 支持样式、图标、徽章和自定义 header 布局

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读