MDC-104 Android:Material 高级组件 (Kotlin)

logo_components_color_2x_web_96dp.png

Material Components (MDC) 有助于开发者实现 Material Design。MDC 是由一组 Google 工程师和用户体验设计人员倾心打造的,提供数十种精美实用的界面组件,可用于 Android、iOS、Web 和 Flutter。

如需了解详情,请访问 material.io/develop

在 Codelab MDC-103 中,您通过自定义 Material Components (MDC) 的颜色、高度和字体排版,设置了应用的样式。

Material Design 系统中的组件可执行一组预定义的任务,并具有某些特征(例如按钮)。不过,按钮不仅是用户执行操作的方式,还是形状、大小和颜色的视觉表达,可以让用户知道,按钮是可以互动的,并且轻触或点击后会触发一些行为。

Material Design 准则从设计人员的角度对组件进行了介绍,说明了可在各个平台上使用的多种基本功能,以及构成每个组件的结构性元素。例如,背景幕包含后层及其内容、前层及其内容、动作规则和显示选项。其中每个组件都可以根据每个应用的需求、用例和内容进行自定义。这些组件主要包括传统视图、控件,以及平台 SDK 的功能。

虽然 Material Design 准则列出了许多组件,但其中有些组件并不适用于可重用代码,因此您在 MDC 中找不到这些组件。您可以全部使用传统代码自行打造这些体验,为您的应用实现自定义样式。

构建内容

在本 Codelab 中,您将向 Shrine 添加背景幕。它会按类别过滤非对称网格中显示的商品。您将使用:

  • 形状
  • 动作
  • 传统的 Android SDK 类

本 Codelab 中用到的 MDC-Android 组件

  • 形状

所需条件

  • 已掌握 Android 开发方面的基础知识
  • Android Studio(如果尚未安装,请在此处下载)
  • Android 模拟器或设备(可通过 Android Studio 获取)
  • 示例代码(参见下一步)

您如何评价自己在构建 Android 应用方面的经验水平?

新手水平 中等水平 熟练水平

接着 MDC-103 继续操作?

如果您已完成 MDC-103,您的代码应该就能用于本 Codelab 了。请跳到第 3 步。

从头开始?

下载起始 Codelab 应用

下载起始应用

起始应用位于 material-components-android-codelabs-104-starter/kotlin 目录中。请务必先通过 cd 命令转到该目录,然后再开始操作。

…或从 GitHub 克隆

如需从 GitHub 克隆此 Codelab,请运行以下命令:

git clone https://github.com/material-components/material-components-android-codelabs
cd material-components-android-codelabs/
git checkout 104-starter

在 Android Studio 中加载起始代码

  1. 在设置向导完成且系统显示 Welcome to Android Studio 窗口后,点击 Open an existing Android Studio project。转到您安装了示例代码的目录,然后依次选择 kotlin -> shrine(或在计算机上搜索 shrine),以打开 Shipping 项目。
  2. 等待 Android Studio 构建和同步项目,如 Android Studio 窗口底部的 activity 指示器所示。
  3. 此时,由于缺少 Android SDK 或构建工具,因此 Android Studio 可能会显示一些构建错误(如下所示)。按照 Android Studio 中的说明安装/更新这些内容,并同步您的项目。

添加项目依赖项

项目需要一个 MDC Android 支持库的依赖项。您下载的示例代码应该已经列出了此依赖项,但您最好按照以下步骤操作,以确保万无一失。

  1. 转到 app 模块的 build.gradle 文件,并确保 dependencies 代码块包含 MDC Android 的依赖项:
api 'com.google.android.material:material:1.1.0-alpha06'
  1. (可选)如有必要,请修改 build.gradle 文件以添加以下依赖项,并同步项目。
dependencies {
    api 'com.google.android.material:material:1.1.0-alpha06'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'com.android.volley:volley:1.1.1'
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:core:1.1.0'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test:runner:1.2.0-alpha05'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha05'
}

运行起始应用

  1. 确保“Run / Play”按钮左侧的 build 配置是 app
  2. 按绿色的“Run / Play”按钮,以构建并运行该应用。
  3. Select Deployment Target 窗口中,如果已有 Android 设备列在可用设备列表中,请跳至第 8 步。否则,请点击 Create New Virtual Device
  4. Select Hardware 屏幕中,选择一个手机设备(如 Pixel 2),然后点击 Next
  5. System Image 屏幕中,选择较新的 Android 版本,最好选择最高的 API 级别。如果相应版本尚未安装,请点击随即显示的 Download 链接,然后完成下载。
  6. 点击 Next
  7. Android Virtual Device (AVD) 屏幕上,让各项设置保持不变,然后点击 Finish
  8. 从部署目标对话框中选择一个 Android 设备
  9. 点击 Ok
  10. Android Studio 会构建并部署该应用,然后自动在目标设备上将其打开。

大功告成!您应该会看到在您设备上运行的 Shrine 应用。

背景幕是应用最靠后的表面,显示在所有其他内容和组件的后面。它由两个表面组成:后层(用于显示操作和过滤器)和前层(用于显示内容)。您可以使用背景幕来显示互动信息和操作,例如导航或内容过滤器。

隐藏网格内容

shr_product_grid_fragment.xml 中,将 android:visibility="gone" 属性添加到 NestedScrollView,以暂时移除商品内容:

shr_product_grid_fragment.xml

<androidx.core.widget.NestedScrollView
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:layout_marginTop="56dp"
   android:background="@color/productGridBackgroundColor"
   android:elevation="8dp"
   android:visibility="gone"
   app:layout_behavior="@string/appbar_scrolling_view_behavior">

我们将在此区域中安装背景幕。为避免在顶部应用栏与背景幕上显示的菜单内容之间显示分隔线,我们将对背景幕使用与顶部应用栏相同的颜色。

shr_product_grid_fragment.xml 中,将以下内容添加为根 FrameLayout 中的第一个元素,就放在 AppBarLayout 的前面:

shr_product_grid_fragment.xml

<LinearLayout
   style="@style/Widget.Shrine.Backdrop"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:gravity="center_horizontal"
   android:orientation="vertical"
   android:paddingTop="100dp"
   android:paddingBottom="100dp">

</LinearLayout>

styles.xml 中,添加以下代码:

styles.xml

<style name="Widget.Shrine.Backdrop" parent="">
   <item name="android:background">?attr/colorAccent</item>
</style>

干得好!您已在 Shrine 的界面中添加了精美的背景幕。接下来,我们将添加菜单。

添加菜单

菜单本质上是文本按钮的列表。我们将在此处添加一个。

res -> layout 目录中创建一个名为 shr_backdrop.xml 的新布局文件,并添加以下内容:

shr_backdrop.xml

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">

   <com.google.android.material.button.MaterialButton
       style="@style/Widget.Shrine.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/shr_featured_label" />

   <com.google.android.material.button.MaterialButton
       style="@style/Widget.Shrine.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/shr_apartment_label" />

   <com.google.android.material.button.MaterialButton
       style="@style/Widget.Shrine.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/shr_accessories_label" />

   <com.google.android.material.button.MaterialButton
       style="@style/Widget.Shrine.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/shr_shoes_label" />

   <com.google.android.material.button.MaterialButton
       style="@style/Widget.Shrine.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/shr_tops_label" />

   <com.google.android.material.button.MaterialButton
       style="@style/Widget.Shrine.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/shr_bottoms_label" />

   <com.google.android.material.button.MaterialButton
       style="@style/Widget.Shrine.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/shr_dresses_label" />

   <View
       android:layout_width="56dp"
       android:layout_height="1dp"
       android:layout_margin="16dp"
       android:background="?android:attr/textColorPrimary" />

   <com.google.android.material.button.MaterialButton
       style="@style/Widget.Shrine.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/shr_account_label" />

</merge>

使用 <include> 标记将此列表添加到您刚刚在 shr_product_grid_fragment.xml 中添加的 LinearLayout

shr_product_grid_fragment.xml

<LinearLayout
   style="@style/Widget.Shrine.Backdrop"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:gravity="center_horizontal"
   android:orientation="vertical"
   android:paddingTop="88dp">

   <include layout="@layout/shr_backdrop" />
</LinearLayout>

构建并运行。您的主屏幕应如下所示:

现在,我们已完成背景幕的设置。接下来,让我们来恢复之前隐藏的内容。

在本 Codelab 中对 Shrine 进行任何更改之前,主要商品内容位于最靠后的表面。在添加背景幕后,由于这个内容是显示在背景幕的前面,因此该内容现在更加突出了。

添加新层

我们应该再次显示商品网格层。请从 NestedScrollView 中移除 android:visibility="gone" 属性:

shr_product_grid_fragment.xml

<androidx.core.widget.NestedScrollView
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:layout_marginTop="56dp"
   android:background="@color/productGridBackgroundColor"
   android:elevation="8dp"
   app:layout_behavior="@string/appbar_scrolling_view_behavior">

我们来设置前层的样式,让左上角显示一个缺口。Material Design 将这种类型的自定义项称为形状。Material 表面可以用不同的形状显示。形状为表面增加了重点和风格,可用于展现品牌形象。Material 形状可以有弧形或尖锐的角和角的两条边,以及任意数量的边;可以是对称的,也可以是不规则的。

添加形状

修改网格的形状。我们提供了自定义的形状背景,但这种形状只有在 Android Marshmallow 及更高版本上才能显示正确。因此,我们只能在 Android Marshmallow 及更高版本上的 NestedScrollView 上设置 shr_product_grid_background_shape 背景。首先,将 id 添加到 NestedScrollView,以便我们在代码中对其进行引用,如下所示:

shr_product_grid_fragment.xml

<androidx.core.widget.NestedScrollView
   android:id="@+id/product_grid"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:layout_marginTop="56dp"
   android:background="@color/productGridBackgroundColor"
   android:elevation="8dp"
   app:layout_behavior="@string/appbar_scrolling_view_behavior">

然后,在 ProductGridFragment.kt 中以编程方式设置背景。添加以下逻辑,以在 onCreateView() 的末尾(就在 return 语句之前)设置该背景:

ProductGridFragment.kt

// Set cut corner background for API 23+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       view.product_grid.background = context?.getDrawable(R.drawable.shr_product_grid_background_shape)
}

最后,我们将更新 productGridBackgroundColor 颜色资源(自定义形状背景也会使用该资源),如下所示:

colors.xml

<color name="productGridBackgroundColor">#FFFBFA</color>

构建并运行:

我们已为 Shrine 的主要表面提供了采用自定义样式的形状。由于表面有高度,用户可以看到前方的白色层后面还有内容。我们接下来要添加动作,让用户可以看到那里的内容,也就是菜单。

动作是一种可以让应用生动起来的方式。动作可以热烈奔放,也可以舒缓轻柔,还可以介于两者之间。您使用的动作类型应当适合具体情况。应用于重复性、规律性操作的动作应轻柔,以免操作经常耗费过多时间。其他情况(比如用户首次打开应用时)则适合使用更加抢眼的动作,以便让用户了解如何使用您的应用。

向菜单按钮添加显现动作

该动作是指在正对面垂直向下移动的形状。我们已为您提供了一个点击监听器,用于在 NavigationIconClickListener.kt 中完成动作条的平移动画。我们可以在 ProductGridFragementonCreateView()(负责设置工具栏的部分)中设置此点击监听器。请添加以下代码行,以在工具栏的菜单图标上设置点击监听器:

ProductGridFragment.kt

view.app_bar.setNavigationOnClickListener(NavigationIconClickListener(activity!!, view.product_grid))

该部分现在应如下所示:

ProductGridFragment.kt

// Set up the toolbar.
(activity as AppCompatActivity).setSupportActionBar(view.app_bar)
view.app_bar.setNavigationOnClickListener(NavigationIconClickListener(activity!!, view.product_grid))

构建并运行。按菜单按钮:

再次按导航菜单图标应该能隐藏它。

调整前层的动作

动作是表达品牌的绝佳方式。我们来看看使用不同时间曲线时,显现动画是什么样的。

更新 ProductGridFragment.kt 中的点击监听器,以将插值器传递给导航图标的点击监听器,如下所示:

ProductGridFragment.kt

view.app_bar.setNavigationOnClickListener(NavigationIconClickListener(activity!!, view.product_grid, AccelerateDecelerateInterpolator()))

这会产生不同的效果,不是吗?

品牌图标也可以扩大到我们熟悉的图标。接下来,我们要将显现图标设为自定义并将其与我们的标题合并,形成独特的品牌外观。

更改菜单按钮图标

更改菜单按钮以显示包含钻石设计的图标。更新 shr_product_grid_fragment.xml 中的工具栏,以使用我们为您提供的全新品牌图标 (shr_branded_menu),并设置 app:contentInsetStartandroid:padding 属性,以便让工具栏更符合设计人员的规范:

shr_product_grid_fragment.xml

<androidx.appcompat.widget.Toolbar
   android:id="@+id/app_bar"
   style="@style/Widget.Shrine.Toolbar"
   android:layout_width="match_parent"
   android:layout_height="?attr/actionBarSize"
   android:paddingStart="12dp"
   android:paddingLeft="12dp"
   android:paddingEnd="12dp"
   android:paddingRight="12dp"
   app:contentInsetStart="0dp"
   app:navigationIcon="@drawable/shr_branded_menu"
   app:title="@string/shr_app_name" />

我们将再次更新 ProductGridFragment.ktonCreateView() 的点击监听器,以便在菜单打开和关闭时接受工具栏的可绘制对象,如下所示:

ProductGridFragment.kt

// Set up the toolbar.
(activity as AppCompatActivity).setSupportActionBar(view.app_bar)
view.app_bar.setNavigationOnClickListener(NavigationIconClickListener(
       activity!!,
       view.product_grid,
       AccelerateDecelerateInterpolator(),
       ContextCompat.getDrawable(context!!, R.drawable.shr_branded_menu), // Menu open icon
       ContextCompat.getDrawable(context!!, R.drawable.shr_close_menu))) // Menu close icon

构建并运行:

很好!当背景幕可以显现时,系统会显示钻石形状的菜单图标;当菜单可以隐藏时,系统会改为显示关闭图标。

在学习这四个 Codelab 的过程中,您已经了解如何使用 Material Components 打造独特、优质的用户体验,表现品牌的个性和风格。

后续步骤

我们已学完此 Codelab (MDC-104),本 Codelab 系列到此结束。您可以访问 MDC-Android 组件目录,探索 MDC-Android 中的更多组件。

如需完成此 Codelab 的进一步挑战,请修改您的 Shrine 应用,以更改从背景幕菜单中选择某个类别后显示的商品图片。

如需了解如何将此应用关联到 Firebase 以使用可正常工作的后端,请参阅 Firebase Android Codelab

我能够用合理的时间和精力完成此 Codelab

非常同意 同意 一般 不同意 非常不同意

我希望日后继续使用 Material Components

非常同意 同意 一般 不同意 非常不同意