Material Design 实战(一):理念入门与 Toolbar 核心实践

287 阅读4分钟

什么是 Material Design

Material Design 是由 Google 开发的一套全面的设计系统,它不仅是一套视觉规范,还包含了视觉、运动、互动效果等指导原则。我们可以使用 Google 提供的 Material 库来轻松地将自己的应用 Material 化。

现在,我们就来学习使用这个库,并且配合 AndroidX 中的控件来完成一个 Material Design 应用。

前置工作:

  1. 新建一个名为 MaterialTestEmpty Views Activity 项目。
  2. 将图片资源放到 drawable-xxhdpi 目录中,资源下载

初识 Toolbar

对于 Toolbar 你可能很陌生,但是说到 ActionBar 控件,你就非常熟悉了,它就是每个界面顶部的那个标题栏。但由于 ActionBar 在设计上不够灵活,它被限定了只能位于屏幕顶部,很难实现丰富的 Material Design 的效果。所以官方早就不推荐使用 ActionBar 了,而是更推荐 Toolbar

Toolbar 控件相比 ActionBar 功能更加强大,而且可以被放置到界面的任何位置。

现在,新建的项目中默认是没有标题栏的。为什么呢?我们来查看 AndroidManifest.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>

    <application
        ...
        android:theme="@style/Theme.MaterialTest"
        ...>
       
        ...
    </application>

</manifest>

可以看到 android:theme 属性指向了 res/values/themes.xml 文件中定义的主题:

<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Base.Theme.MaterialTest" parent="Theme.Material3.DayNight.NoActionBar">
        </style>

    <style name="Theme.MaterialTest" parent="Base.Theme.MaterialTest" />
</resources>

该主题的 parent 主题为 Theme.Material3.DayNight.NoActionBar。说明这是一个默认不带标题栏的主题。

为什么默认要设置为 NoActionBar 的主题呢?

因为官方希望给予开发者最大的自由度。把 Toolbar 当做一个普通控件,我们可以自由地将其放置到布局的任意位置,并且配合其他控件实现更丰富的 Material Design 效果,比如滑动折叠的标题栏。

接下来,我们来定义应用的主题。简单起见,我们使用浅色主题,修改 themes.xml 文件:

<resources>
    <style name="Base.Theme.MaterialTest" parent="Theme.Material3.Light.NoActionBar">
        <item name="colorPrimary">@color/app_primary</item>
        <item name="colorOnPrimary">@color/app_on_primary</item>
        <item name="android:statusBarColor">@color/app_status_bar</item>
    </style>

    <style name="Theme.MaterialTest" parent="Base.Theme.MaterialTest" />
</resources>

然后,在 res/values/colors.xml 文件中,添加这些颜色的定义:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="app_primary">#6516f5</color>
    <color name="app_on_primary">#FFFFFF</color>
    <color name="app_status_bar">#4f13db</color>
</resources>

我们来解释一下我们刚刚重写的属性:

  • colorPrimary: 主要颜色。通常用于 Toolbar、重要按钮等关键组件的背景色。

  • colorOnPrimary: 在主要颜色之上的内容颜色。

  • android:statusBarColor: 状态栏颜色。

现在,我们在布局中放置一个 Toolbar 控件,activity_main.xml 中的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">

    <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.Material3.Dark.ActionBar"
        app:popupTheme="@style/ThemeOverlay.MaterialComponents.Light" />

</FrameLayout>

这里的 ?attr/actionBarSize 是引用主题属性的写法,会获取当前主题中定义的标准标题栏的高度。同样,我们的背景色也引用了主题中的 colorPrimary 属性。当想换主题色时,只需修改 themes.xml 文件即可,所有使用该属性的控件颜色都会改变。

我们需要 Toolbar 上面的标题和图标是浅色的,所以我们使用 android:theme 属性来将主题修改为暗色。并且我们希望弹出菜单是浅色的,所以使用了 app:popupTheme 属性来修改弹出菜单的主题为浅色。

最后,回到 MainActivity 中使用 Toolbar,代码如下:

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        // 将我们布局中的 Toolbar 设置为 Activity 的 Action Bar
        setSupportActionBar(binding.toolbar)
    }
}

我们通过 setSupportActionBar() 方法,将 Activity 所有与 ActionBar 相关的操作(比如菜单创建)交给这个 Toolbar 来处理。

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

image.png

Toolbar 的常用功能

接下来,我们再来看看 Toolbar 常用的功能。比如修改标题栏上显示的内容,我们可以在 AndroidManifest.xml 文件中指定:

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>

    <application ...>
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="Fruits">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

我们在 <activity> 标签中新增了一个 label 属性,用于指定 Toolbar 中显示的内容。默认会使用 <application> 标签中的 label 属性值,也就是应用名称。

我们还可以给 Toolbar 新增一些可交互的菜单按钮。首先,创建一个 res/menu 文件夹,在其中创建一个 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> 标签定义了三个菜单项,关键在于 app:showAsAction 属性,它决定了按钮的显示位置。

  • always:总是尝试显示在 Toolbar 中。最好不使用,因为空间不足时会导致布局混乱。
  • ifRoom:如果 Toolbar 的可用空间足够,就显示为图标按钮;如果空间不足,就收起到在菜单中。这是最常用的选项。
  • never:永远不出现在 Toolbar 中,总是显示在菜单中。用于不常用的操作。

接着,在 MainActivity 中,加载菜单布局并处理菜单项的点击事件。代码如下:

class MainActivity : AppCompatActivity() {

    ...

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        // 加载我们定义的菜单资源
        menuInflater.inflate(R.menu.toolbar, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // 通过菜单项 id 判断点击的是哪个菜单项
        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
    }
}

现在运行程序,界面如图所示:

image.png

点击最右侧的菜单按钮,就可以展开被折叠的菜单项了。

image.png

至此,Toolbar 的基本用法已经讲完了。

未完待续...