26、Material Design入门:Toolbar、FloatingActionButton、Snackbar、NavigationView、TabLayout
Material Design 是 Google 在 2014 年推出的一套设计语言。说白了,它就是想统一 Android、Web 和 iOS 上的视觉体验。我刚开始接触那会儿,觉得不就是换个皮肤嘛。后来真正做项目才发现,这套规范背后藏着很多交互细节和性能考量。
今天咱们就聊五个最常用的 Material 组件:Toolbar、FloatingActionButton、Snackbar、NavigationView 和 TabLayout。这些都是日常开发中几乎必用的东西,掌握了它们,你的 App 至少能有个「专业相」。
Toolbar:取代 ActionBar 的正确姿势
老 Android 开发者都知道 ActionBar,但 Google 后来推荐用 Toolbar。为什么?因为 Toolbar 更灵活。它可以放在布局的任何位置,可以嵌套,可以自定义样式。
我个人习惯在 styles.xml 里先把默认的 ActionBar 干掉:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
然后在布局里加上 Toolbar:
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:title="首页"
app:titleTextColor="@android:color/white"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
在 Activity 里记得调用 setSupportActionBar(toolbar)。嗯,这里要注意:如果你用了 NoActionBar 主题,但忘了调用这个方法,Toolbar 就只是个普通 View,不会拥有 ActionBar 的特性(比如菜单回调)。
app:contentInsetStart="0dp" 去掉默认的左边距,然后直接往 Toolbar 里塞 EditText 就行。Toolbar 本质上是个 ViewGroup,可以放子 View。
FloatingActionButton:悬浮按钮的讲究
FloatingActionButton(简称 FAB)是 Material Design 的标志性组件。它代表页面上最核心的操作。比如邮件 App 的「写邮件」按钮,地图 App 的「定位」按钮。
用法很简单:
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/ic_add"
app:backgroundTint="@color/colorAccent" />
但有几个坑我得提醒你:
- 不要滥用 FAB。一个页面最多一个 FAB。放多了用户会困惑。
- 注意 z 轴高度。FAB 默认有 elevation,如果页面里有 BottomSheet 或 Dialog,可能会出现层级遮挡。我曾经遇到过 FAB 被 Dialog 盖住一半的情况,后来用
android:elevation="8dp"解决了。 - 动画要平滑。FAB 的显示/隐藏建议用
FloatingActionButton.show()和hide(),它们自带缩放动画。别自己写 Visibility 切换,太生硬。
app:layout_anchor 属性,让 FAB 锚定在某个 View 上,或者用 app:layout_behavior 实现滚动时自动隐藏。
Snackbar:比 Toast 更优雅的反馈
Snackbar 是 Toast 的升级版。它从底部弹出,可以带一个操作按钮。比如「删除成功」后面跟一个「撤销」按钮。
Snackbar.make(view, "数据已删除", Snackbar.LENGTH_LONG)
.setAction("撤销") {
// 恢复数据
}
.show()
注意 make() 的第一个参数必须是 CoordinatorLayout 或者它的子类。为什么?因为 Snackbar 需要依赖 CoordinatorLayout 来实现滑动关闭和自动偏移(比如避开 FAB)。
如果你传了一个普通的 View,Snackbar 也能弹出来,但不会有滑动关闭的效果,也不会自动避开 FAB。嗯,这就是很多新手觉得 Snackbar「不好用」的原因——其实是布局没搭对。
CoordinatorLayout → AppBarLayout + Toolbar → 内容区域 → FAB
NavigationView:侧滑菜单的标准实现
NavigationView 是 DrawerLayout 的标配。它提供了一个标准的侧滑菜单模板,支持 header 和 menu 两部分。
布局示例:
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 主内容区域 -->
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/nav_menu"
app:headerLayout="@layout/nav_header" />
</androidx.drawerlayout.widget.DrawerLayout>
menu 文件里可以定义分组和 item:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item android:id="@+id/nav_home" android:title="首页" android:icon="@drawable/ic_home" />
<item android:id="@+id/nav_settings" android:title="设置" android:icon="@drawable/ic_settings" />
</group>
</menu>
我个人习惯在 onNavigationItemSelected 里用 item.isChecked = true 来标记选中状态。但要注意:如果你用了 checkableBehavior="single",系统会自动处理选中态,你只需要返回 true 就行。
nav_view.getHeaderView(0) 来获取 header 布局。
TabLayout:标签页的优雅方案
TabLayout 通常和 ViewPager 或 ViewPager2 配合使用。它提供了 Material 风格的标签切换效果。
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabGravity="fill"
app:tabIndicatorColor="@color/colorAccent"
app:tabSelectedTextColor="@color/colorAccent" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
绑定代码:
val adapter = ViewPagerAdapter(this)
view_pager.adapter = adapter
TabLayoutMediator(tab_layout, view_pager) { tab, position ->
tab.text = "标签${position + 1}"
}.attach()
这里有个细节:TabLayoutMediator 是 Material 库提供的桥梁类。它负责把 TabLayout 和 ViewPager2 同步起来。如果你用 ViewPager(旧版),可以用 tab_layout.setupWithViewPager()。但 ViewPager2 必须用 Mediator。
你想想看,如果不用 Mediator,手动监听 ViewPager2 的页面切换事件,再手动更新 TabLayout 的选中状态,那代码得多啰嗦。
tabMode 有两种:fixed 和 scrollable。标签少用 fixed,标签多用 scrollable。我见过有人把 10 个标签全用 fixed 模式,结果每个标签被压缩得只剩两个字,根本看不清。这种问题其实在 UI 评审阶段就该发现。
知识体系总览
下面这张图帮你理清这五个组件的关系和典型用法:
组件对比速查表
| 组件 | 主要用途 | 依赖布局 | 常见坑点 |
|---|---|---|---|
| Toolbar | 顶部导航栏 | AppBarLayout | 忘记 setSupportActionBar |
| FloatingActionButton | 核心操作按钮 | CoordinatorLayout | z 轴遮挡、滥用 |
| Snackbar | 操作反馈 | CoordinatorLayout | 参数传错 View 类型 |
| NavigationView | 侧滑菜单 | DrawerLayout | header 点击事件处理 |
| TabLayout | 标签页切换 | ViewPager/ViewPager2 | tabMode 选择不当 |
好了,这五个组件是 Material Design 的入门必修课。说白了,它们解决的都是 App 里最常见的交互场景:导航、操作、反馈、菜单、切换。把这些组件用熟了,你的 App 至少看起来像个「正经」的 Material Design 应用。
我个人建议你动手搭一个 Demo,把 Toolbar、FAB、Snackbar、NavigationView 和 TabLayout 全部塞到一个页面里。用 CoordinatorLayout 把它们串起来。你会发现,这些组件之间的联动其实很巧妙——比如 Snackbar 弹出时 FAB 会自动上移,TabLayout 切换时 ViewPager 会跟着滑动。这些细节,就是 Material Design 的魅力所在。