12.2--Toolbar

    技术2026-02-11  30

    Toolbar 将会是我们本章接触的第一个控件,是由AndroidX 库提供的。虽说对于 Toolbar 你暂时应该还是比较陌生的,但是对于它的另一个相关控件 ActionBar ,你就应该有点熟悉了。

    回忆一下,我们曾经在 4.4.1 小节为了使用一个自定义的标题栏,而隐藏了系统原生的 ActionBar。没错,每个Activity 最顶部的那个标题栏其实就是 ActionBar,之前我们编写的所有程序里直都有它的身影。

    不过ActionBar 由于其设计的原因,被限定只能位于 Activity 的顶部,从而不能实现一些 Material Design 的效果,因此官方现在已经不再建议使用ActionBar 了。那么本书中我也就不准备再介绍ActionBar 的用法了,而是直接讲解现在更加推荐使用的Toolbar。

    首先你要知道,任何一个新建的项目,默认都会显示ActionBar 的,这个想必你已经见识过太多次了。那么这个ActionBar 到底是从哪里来的呢?其实这事根据项目中指定的主题来显示的。打开Androidmanifest.xml 文件看一下,如下所示:

    <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.materialtest"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

    可以看到,这里使用 android: theme 属性指定了一个 AppTheme 的主题。那么这个 AppTheme 又是在哪里定义的呢?打开 res/values/styles.xml 文件,代码如下所示:

    <resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources>

    这里定义了一个叫 AppTheme 的主题,然后指定它的 parent 主题是 Theme. AppCompat.Light.DarkActionbar。这个 DarkActionbar 是一个深色的 Actionbar 主题,我们之前所有的项目中自带的 Actionbar 就是因为指定了这个主题才出现的。

    而现在我们准备使用 lobar 来替代 Actionbar,因此需要指定一个不带 Actionbar 的主题通常有 Theme. AppCompat.Noaction Bar 和 Theme. AppCompat. Light.NoactionBar这两种主题可选。其中 Theme.App Compat.NoactionBar 表示深色主题,它会将界面的主体颜色设成深色,陪衬颜色设成淡色。而 Theme.AppCompat. Light.NoactionBar 表示淡色主题,它会将界面的主体颜色设成淡色,陪衬颜色设成深色。具体的效果你可以自己动手试一试,这里由于我们之前的程序一直都是以淡色为主的,那么我就选用淡色主题了,如下所示:

    <resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources>

    然后观察一下 Apptheme 中的属性重写,这里重写了 colorPrimary、colorPrimarydark 和 colorAccent 这 3 个属性的颜色。那么这 3 个属性分别代表着什么位置的颜色呢?我用语言比较难描述清楚,还是通过一张图来理解一下吧,如图 12.2 所示。

    可以看到,每个属性所指定颜色的位置直接一目了然了。

    除了上述 3 个属性之外,我们还可以通过 textcolorPrimary、windowBackground 和 navigationBarColor 等属性来控制更多位置的颜色。不过唯独 colorAccent 这个属性比较难理解,它不只是用来指定这样一个按钮的颜色,而是更多表达了一个强调的意思,比如一些控件的选中状态也会使用 colorAccent 的颜色。

    现在我们已经将 ActionBar 隐藏起来了,那么接下来看一看如何使用 Toolbar 来替代 ActionBar,。修改 activity_main.xml 中的代码,如下所示:

    <LinearLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> </LinearLayout>

    虽然这段代码不长,但是里面着实有不少技术点是需要我们仔细琢磨一下的。首先看一下第2行,这里使用xmlns:app 指定了一个新的命名空间。思考一下,正是由于每个布局文件都会使用xmlns:android 来指定一个命名空间,我们才能一直使用android:id、android:layout_width 等写法。这里指定了xmlns:app ,也就是说现在可以使用app:attribute 这样的写法了。但是为什么这里要一个xmlns:app 的命名空间呢?这是由于许多Material 属性是在新系统中新增的,老系统中并不存在,那么为了能够兼容老系统,我们就不能使用android:attribute 这样的写法了,而是应该使用app:attribute。

    接下来定义了一个Toolbar 控件,这个控件是由 appcompat 库提供的。这里我们给Toolbar 指定了一个id,将它的宽度设置为match_parent,高度设置为actionBar 的高度,背景色设置为colorPrimary。 不过下面的部分就稍稍有点难理解了,由于我们刚才在style.xml 中将程序的主题指的成了浅色主题,因此Toolbar 现在也是浅色主题,那么Toolbar 上面的各种元素就会自动使用深色系,从而和主题颜色区别开。但是之前使用ActionBar 时文字都是白色的,现在变成黑色的会很难看。那么为了能让Toolbar 单独使用深色主题,这里我们使用了android:theme 属性,将Toolbar 的主题指定成了 ThemeOverlay.AppCompat.Dark.ActionBar 。但是这样指定之后又会出现新的问题,如果Toolbar 中有菜单按钮(我们在3.2.5 小节中学过),那么弹出的菜单项也会变成深色主题,这样就再次变得十分难看了,于是这里又使用了app:popupTheme 属性,单独将弹出的菜单指定成了浅色主题。

    如果你觉得上面的描素很绕的话,可以自己动手做一做实验,看看不指定上诉主题会是什么样的效果,这样你会理解得更加深刻。

    写完了布局,接下来我们修改MainActivity,代码如下所示:

    class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) } }

     这里关键的代码只有一句,调用setSupportActionBar() 方法并将Toolbar 的实例传入,这样我们就做到既使用了Toolbar,又让它的外观与功能都和ActionBar 一致了。

    现在运行一下程序,效果如图:

    这个标题栏我们再熟悉不过了,虽然看上去和之前的标题栏没什么两样,但其实它已经是Toolbar 而不是ActionBar 了。因此它现在也具备了实现Material Design 效果的能力,这个我们在后面就会学到。

    接下来我们再学习一些 Toolbar 比较常用的功能吧,比如修改标题栏上显示的文字内容。这段文字内容是在 AndroidManifest.xml 中指定的,如下所示:

    <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.materialtest"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="Fruits" > ... </activity> </application> </manifest>

    这里给 activity 増加了一个 android: label 属性,用于指定在 Toolbar 中显示的文字内容,如果没有指定的话,会默认使用 application 中指定的 label 内容,也就是我们的应用名称。

    不过只有一个标题的 polar 看起来太单调了,我们还可以再添加一些 action 按钮来让Toolbar 更加丰富一些,这里我提前准备了几张图片来作为按钮的图标,将它们放在了 drawable--xxhdpi 目录下。现在右击 res 目录→New → Directory,创建一个 menu 文件夹。然后右击 menu 文件夹→ New-Menu resource file, 创建一个 toolbar.xml 文件,并编写如下代码:

    <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/backup" android:icon="@drawable/ic_backup" android:title="Backup" app:showAsAction="always" /> <item android:id="@+id/delete" android:icon="@drawable/ic_delete" android:title="Delete" app:showAsAction="ifRoom" /> <item android:id="@+id/settings" android:icon="@drawable/ic_settings" android:title="Settings" app:showAsAction="never" /> </menu>

    可以看到,我们通过<item>标签来定义 action 按钮,android:id 用于指定按钮的id ,android:icon 用于指定按钮的图标,and roid:title 用于指定按钮的文字。

    接着使用 app: showAsAction 来指定按钮的显示位置,之所以这里再次使用了 app 命名空间,同样是为了能够兼容低版本的系统。showAsAction 主要有以下几种值可选:

    always 表示永远显示在 Toolbar 中,如果屏幕空间不够则不显示;

    ifRoom 表示屏幕空间足够的情况下显示在 Toolbar 中,不够的话就显示在菜单当中;

    never 则表示永远显示在菜单当中。

    注意,Toolbar 中的 action 按钮只会显示图标,菜单中的 action 按钮只会显示文字。

    接下来的做法就和 3.2.5 小节中的完全一致了,修改 MainActivity 中的代码,如下所示:

    class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) } override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.toolbar,menu) return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { when(item.itemId){ R.id.backup -> Toast.makeText(this,"You clicked Backup",Toast.LENGTH_SHORT).show() R.id.delete -> Toast.makeText(this,"You clicked Delete",Toast.LENGTH_SHORT).show() R.id.settings -> Toast.makeText(this,"You clicked Settings",Toast.LENGTH_SHORT).show() } return true } }

    非常简单,我们在onCreateOptionsMenu() 方法中加载了 toolbar.xml 这个菜单文件,然后在onOptionsItemSelected() 方法中处理各个按钮的点击事件。现在重新运行一下程序,效果如图:

    可以看到,Toolbar 上面现在显示了两个 action 按钮,这是因为 Backup 按钮指定的显示位置是 always, Delete 按钮指定的显示位置是 ifRoom,而现在屏幕空间很充足,因此两个按钮都会显示在 Toolbar 中。另外一个 Settings 按钮由于指定的显示位置是 never,所以不会显示在 Toolbar 中,点击一下最右边的菜单按钮来展开菜单项,你就能找到 Settings 按钮了。另外这些 action 按钮都是可以响应点击事件的,你可以自己去试一试。

    好了,关于 Toolbar 的内容就先讲这么多吧。当然 Toolbar 的功能还远远不只这些,不过我们显然无法在一节当中就把所有的用法全部学完,后面会结合其他控件来挖掘 Toolbar 的更多功能。

    Processed: 0.008, SQL: 9