什么是 Material Design
Material Design 是由 Google 开发的一套全面的设计系统,它不仅是一套视觉规范,还包含了视觉、运动、互动效果等指导原则。我们可以使用 Google 提供的 Material 库来轻松地将自己的应用 Material 化。
现在,我们就来学习使用这个库,并且配合 AndroidX 中的控件来完成一个 Material Design 应用。
前置工作:
- 新建一个名为
MaterialTest的 Empty Views Activity 项目。 - 将图片资源放到
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 来处理。
运行一下程序,效果如图:
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
}
}
现在运行程序,界面如图所示:
点击最右侧的菜单按钮,就可以展开被折叠的菜单项了。
至此,Toolbar 的基本用法已经讲完了。
未完待续...