38、Material Design之Toolbar:替换ActionBar、自定义标题、菜单、搜索框
说实话,Android开发里最让我头疼的旧组件之一,就是那个老旧的ActionBar。它太死板了,想改个颜色都得绕好几个弯。后来Google推出了Toolbar,我第一时间就切过去了。这玩意儿说白了就是一个更灵活的View,你想放哪就放哪,想怎么打扮就怎么打扮。
今天我们就来聊聊Toolbar。我会从替换ActionBar开始,再到自定义标题、添加菜单、集成搜索框,一步步带你吃透它。
为什么我们要用Toolbar替换ActionBar?
ActionBar是Android早期就有的东西,它被硬生生地塞进了Activity的装饰视图里。你想动它?得用一堆主题属性去配置,灵活性很差。Toolbar就不一样了,它就是个普通的ViewGroup,你可以把它放在布局的任何位置,甚至嵌套在CoordinatorLayout里做折叠效果。
我个人习惯是,新项目一律不用ActionBar,直接上Toolbar。为什么?因为可控性太强了。你想想看,一个普通的View,你可以随意设置背景、间距、动画,甚至可以在里面塞别的子View——这在ActionBar时代是想都不敢想的。
第一步:干掉ActionBar,启用Toolbar
要使用Toolbar,首先得把系统自带的ActionBar隐藏掉。方法很简单,在主题里设置一下就行。
在 res/values/themes.xml 中,把主题的父类改成不带ActionBar的:
<!-- 使用 NoActionBar 主题,干掉系统ActionBar -->
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
</style>
然后在Activity的布局文件里,把Toolbar加进去:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 这就是我们的Toolbar -->
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<!-- 其他内容 -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="这里是正文内容"
android:gravity="center" />
</LinearLayout>
最后在Activity里,把Toolbar设置成ActionBar:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar) // 这一句是关键
}
}
嗯,这里要注意:setSupportActionBar() 调用之后,Toolbar就拥有了ActionBar的所有能力,比如菜单回调、标题显示、返回导航等。但它本质上还是一个View,你可以随时修改它的属性。
自定义标题:别再用默认的了
默认的标题就是Activity的label,但很多时候我们需要更灵活的控制。比如标题居中对齐、加个副标题、甚至放个自定义的搜索框。
Toolbar提供了几个直接的方法:
toolbar.title = "我的标题"
toolbar.subtitle = "副标题在这里"
toolbar.setTitleTextColor(Color.WHITE)
toolbar.setSubtitleTextColor(Color.LTGRAY)
但如果你想要更复杂的布局,比如标题左边加个图标,右边加个按钮——那就直接在Toolbar里嵌套子View吧。
<androidx.appcompat.widget.Toolbar ...>
<!-- 完全自定义的标题区域 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_logo" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="首页"
android:textSize="18sp"
android:textColor="@android:color/white"
android:layout_marginStart="8dp" />
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
我在项目中遇到过一种情况:产品要求标题在左边,但右边还要显示一个用户头像。用Toolbar嵌套布局,几分钟就搞定了。要是用ActionBar,估计得折腾半天。
菜单:Toolbar的灵魂
Toolbar的菜单机制和ActionBar完全一样,都是通过Menu资源文件来定义的。但Toolbar更灵活——你可以控制菜单项的显示位置,甚至动态添加和移除。
先创建一个菜单文件 res/menu/menu_main.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:icon="@drawable/ic_search"
android:title="搜索"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_settings"
android:title="设置"
app:showAsAction="never" />
<item
android:id="@+id/action_about"
android:title="关于"
app:showAsAction="never" />
</menu>
然后在Activity里重写 onCreateOptionsMenu() 和 onOptionsItemSelected():
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_search -> {
// 处理搜索
true
}
R.id.action_settings -> {
// 跳转到设置
true
}
else -> super.onOptionsItemSelected(item)
}
}
这里有个小技巧:app:showAsAction 控制菜单项是显示在Toolbar上(图标形式),还是隐藏在溢出菜单里(三个点)。我个人习惯把最常用的操作设为 ifRoom,次要的放在溢出菜单里。
actionMenuTextColor 和 drawableTint。
搜索框:集成SearchView
搜索功能是很多App的刚需。Toolbar配合SearchView,可以做出非常流畅的搜索体验。
首先,在菜单文件里把搜索项改成SearchView:
<item
android:id="@+id/action_search"
android:icon="@drawable/ic_search"
android:title="搜索"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" />
注意 collapseActionView 这个flag——它让SearchView默认收起来,点击图标才展开。用户体验很好。
然后在Activity里配置SearchView的监听:
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
val searchItem = menu?.findItem(R.id.action_search)
val searchView = searchItem?.actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
// 用户点击搜索按钮时触发
// 在这里执行搜索逻辑
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
// 用户输入文字时实时触发
// 可以用来做实时搜索建议
return false
}
})
return true
}
我曾经踩过一个坑:SearchView的样式和Toolbar不搭,背景是白色的,在深色Toolbar上特别突兀。解决办法是给SearchView设置一个自定义样式:
<style name="CustomSearchViewStyle" parent="Widget.AppCompat.SearchView">
<item name="android:background">@drawable/search_view_bg</item>
</style>
然后在菜单里引用:
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:searchViewStyle="@style/CustomSearchViewStyle"
androidx.appcompat.widget.SearchView 而不是原生的。
知识体系总览
下面这张图总结了Toolbar的核心知识点和它们之间的关系。你可以把它当作一个快速参考。
避坑指南:我踩过的那些坑
Toolbar虽然好用,但也不是没有坑。我把自己遇到过的几个典型问题列出来,你遇到了可以少走弯路。
| 问题 | 原因 | 解决方案 |
|---|---|---|
| Toolbar标题不显示 | 没有调用 setSupportActionBar() |
确保在Activity中调用了该方法 |
| 菜单项图标不显示 | 主题冲突或图标颜色问题 | 给Toolbar设置 app:theme 为深色主题 |
| SearchView背景突兀 | 默认样式与Toolbar不匹配 | 自定义SearchView样式,设置背景和圆角 |
| Toolbar高度不对 | 使用了固定高度而非 ?attr/actionBarSize |
推荐使用 ?attr/actionBarSize 作为高度 |
我曾经在一个项目里,因为忘了调用 setSupportActionBar(),结果Toolbar的菜单死活不显示。排查了半天才发现是这个问题。你想想看,一个方法就能解决的事,愣是让我折腾了俩小时。所以,记住这个步骤。
总结一下
Toolbar的本质就是一个灵活的View容器。它替换了死板的ActionBar,给了我们自定义标题、菜单、搜索框的能力。核心就三步:
- 用NoActionBar主题干掉系统ActionBar
- 在布局里添加Toolbar并调用
setSupportActionBar() - 通过Menu资源和SearchView实现交互功能
说白了,Toolbar就是让你从系统束缚中解放出来的工具。掌握了它,你的App标题栏就能随心所欲了。
公众号:蓝海资料掘金营,微信deep3321