MDC-101 Android:Material Components (MDC) 基础知识 (Kotlin)

1. 简介

logo_components_color_2x_web_96dp.png

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

什么是 Material Design 和 Material Components for Android?

Material Design 是一个系统,用于构建醒目、美观的数字产品。它采用一套统一的原则和组件,将风格、品牌、交互和动效结合起来,让产品团队得以充分释放其设计潜能。

对于 Android 应用,Material Components for Android(以下简称 MDC Android)用于将设计和工程与组件库结合在一起,从而在应用之间打造一致的体验。随着 Material Design 系统的发展,这些组件会进行相应更新,以确保一致的像素级完美实现,并遵循 Google 的前端开发标准。MDC 同样适用于 Web、iOS 和 Flutter。

在此 Codelab 中,您将使用 MDC Android 的若干组件构建一个登录页面。

您将构建的内容

我们提供了 4 个 Codelab 来引导您构建一款名为 Shrine 的应用,这是其中的第 1 个。Shrine 是一个销售服装和家居用品的电子商务 Android 应用。此 Codelab 将演示如何使用 MDC Android 来自定义组件以体现任何品牌或样式。

在本 Codelab 中,您将为 Shrine 构建一个包含以下内容的登录页面:

  • 两个文本字段,分别用于输入用户名和密码
  • 两个按钮,分别对应“Cancel”(取消)和“Next”(下一步)
  • 应用名称 (Shrine)
  • Shrine 徽标的图片

4cb0c218948144b4.png

本 Codelab 中用到的 MDC Android 组件

  • 文本字段
  • 按钮

所需条件

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

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

新手 中等 熟练

2. 设置您的开发环境

启动 Android Studio

打开 Android Studio 后,您应该会看到一个标题为“Welcome to Android Studio”的窗口。不过,如果这是您第一次启动 Android Studio,请使用默认值完成 Android Studio 设置向导中的步骤。该步骤可能需要几分钟时间才能下载并安装必要的文件,因此您可以让其在后台运行,同时继续执行下一部分的操作。

下载起始 Codelab 应用

起始应用位于 material-components-android-codelabs-101-starter/kotlin 目录中。

…或从 GitHub 克隆

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

git clone https://github.com/material-components/material-components-android-codelabs
cd material-components-android-codelabs/
git checkout 101-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 中的说明安装/更新这些内容,并同步您的项目。

KzoYWC1S7Se7yL8igi1vXF_mbVxAdl2lg5kb7RODrsVpEng0G6U3NK1Qnn0faBBZd2u71yMXioy9tD-7fv3NXvVO4N3EtMMeWDTmqBMMl6egd9R5uXX0T_SKmahbmRor3wZZHX0ByA

添加项目依赖项

项目需要一个 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 登录页面的起始代码。您应该会看到名称“Shrine”以及该名称正下方的 Shrine 徽标。

e7ed014e84755811.png

我们来看一下代码。我们在示例代码中提供了一个简单的 Fragment 导航框架,用于显示 fragment 以及在 fragment 之间导航。

打开 shrine -> app -> src -> main -> java -> com.google.codelabs.mdc.kotlin.shrine 目录中的 MainActivity.kt,其中应包含以下代码:

MainActivity.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment

class MainActivity : AppCompatActivity(), NavigationHost {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.shr_main_activity)

       if (savedInstanceState == null) {
           supportFragmentManager
                   .beginTransaction()
                   .add(R.id.container, LoginFragment())
                   .commit()
       }
   }

   override fun navigateTo(fragment: Fragment, addToBackstack: Boolean) {
       val transaction = supportFragmentManager
               .beginTransaction()
               .replace(R.id.container, fragment)

       if (addToBackstack) {
           transaction.addToBackStack(null)
       }

       transaction.commit()
   }
}

此 activity 会显示 shr_main_activity.xml 中定义的 R.layout.shr_main_activity 布局文件。

您可以看到,在 onCreate(), 中,MainActivity.kt 启动了 Fragment 事务,以显示 LoginFragment。在此 Codelab 中,我们将修改 LoginFragment。该 activity 还实现了一个在 NavigationHost 中定义的 navigateTo(Fragment) 方法,可让任何 fragment 转到其他 fragment。

按住 Command 键的同时点击(或按住 Ctrl 键的同时点击)该 activity 文件中的 shr_main_activity 以打开布局文件,或找到 app -> res -> layout -> shr_main_activity.xml 中的布局文件。

shr_main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/container"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity"/>

在这里,我们会看到一个简单 <FrameLayout>,充当 activity 所显示 fragment 的容器。

接下来,我们来打开 LoginFragment.kt

LoginFragment.kt

package com.google.codelabs.mdc.kotlin.shrine

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment

class LoginFragment : Fragment() {

   override fun onCreateView(
           inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
       // Inflate the layout for this fragment
       val view = inflater.inflate(R.layout.shr_login_fragment, container, false)

       return view
   }
}

LoginFragment 会膨胀 shr_login_fragment 布局文件,并在 onCreateView() 中显示该文件。

现在,我们来看看 shr_login_fragment.xml 布局文件,了解一下登录页面的外观。

shr_login_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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:background="@color/loginPageBackgroundColor"
   tools:context=".LoginFragment">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:clipChildren="false"
       android:clipToPadding="false"
       android:orientation="vertical"
       android:padding="24dp"
       android:paddingTop="16dp">

       <ImageView
           android:layout_width="64dp"
           android:layout_height="64dp"
           android:layout_gravity="center_horizontal"
           android:layout_marginTop="48dp"
           android:layout_marginBottom="16dp"
           app:srcCompat="@drawable/shr_logo" />

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center_horizontal"
           android:layout_marginBottom="132dp"
           android:text="@string/shr_app_name"
           android:textAllCaps="true"
           android:textSize="16sp" />
   </LinearLayout>
</ScrollView>

在这里,我们从顶部能看到一个 <LinearLayout>,其中包含代表 Shrine 徽标的 <ImageView>

在此之后,有一个 <TextView> 标记,代表徽标下方的 Shrine 标签。此标签的文本是一个名为 @string/shr_app_name字符串资源。如果您在按住 Command 键的同时点击(或按住 Ctrl 键的同时点击)该字符串资源名称,或者打开 app -> res -> values -> strings.xml,则可看到其中定义了字符串资源的 strings.xml 文件。以后添加更多字符串资源时,将在此处进行定义。此文件中的每个资源都应带有 shr_ 前缀,以指明它们是 Shrine 应用的一部分。

现在,您已经熟悉了起始代码,接下来我们要实现第一个组件。

3. 添加文本字段

首先,我们将为登录页面添加两个文本字段,供用户输入用户名和密码。我们将使用 MDC 文本字段组件,其中包括可显示浮动标签和错误消息的内置功能。

d83c47fb4aed3a82.png

添加 XML

shr_login_fragment.xml 中,在 <LinearLayout> 内的“SHRINE”标签 <TextView> 的下方,添加两个包含子元素 TextInputEditTextTextInputLayout 元素:

shr_login_fragment.xml

<com.google.android.material.textfield.TextInputLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_username">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/password_text_input"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_password">

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/password_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>

上面的代码段代表两个文本字段,每个字段都包含一个 <TextInputLayout> 元素和一个 <TextInputEditText> 子元素。每个文本字段的提示文本都是在 android:hint 属性中指定。

我们已为文本字段加入了两个新的字符串资源(即 @string/shr_hint_username@string/shr_hint_password),打开 strings.xml 就能看到这些字符串资源。

strings.xml

<string name="shr_hint_username">Username</string>
<string name="shr_hint_password">Password</string>

添加输入验证功能

TextInputLayout 组件提供内置错误反馈功能。

如需显示错误反馈,请对 shr_login_fragment.xml 进行以下更改:

  • 密码TextInputLayout 元素中,将 app:errorEnabled 属性设置为“true”。这会为文本字段下方的错误消息添加额外的内边距。
  • 对于 Password TextInputEditText 元素,将 android:inputType 属性设置为“textPassword”。这会隐藏密码字段中的输入文本。

完成这些更改后,shr_login_fragment.xml 中的文本字段应如下所示:

shr_login_fragment.xml

<com.google.android.material.textfield.TextInputLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_username">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/password_text_input"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_password"
   app:errorEnabled="true">

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/password_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>

现在,请尝试运行该应用。您应该会看到一个包含两个文本字段(“Username”和“Password”)的页面!

查看悬浮标签动画:

333184b615aed4f7.gif

4. 添加按钮

接下来,我们将为登录页面添加两个按钮:“Cancel”和“Next”。我们将使用 MDC 按钮组件,该组件内置了标志性的 Material Design 水墨涟漪效果。

4cb0c218948144b4.png

添加 XML

shr_login_fragment.xml 中,将 <RelativeLayout> 添加到 TextInputLayout 元素下方的 <LinearLayout> 中。然后,向 <RelativeLayout> 添加两个 <MaterialButton> 元素。

生成的 XML 文件应如下所示:

shr_login_fragment.xml

<RelativeLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <com.google.android.material.button.MaterialButton
       android:id="@+id/next_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentEnd="true"
       android:layout_alignParentRight="true"
       android:text="@string/shr_button_next" />

   <com.google.android.material.button.MaterialButton
       android:id="@+id/cancel_button"
       style="@style/Widget.MaterialComponents.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginEnd="12dp"
       android:layout_marginRight="12dp"
       android:layout_toStartOf="@id/next_button"
       android:layout_toLeftOf="@id/next_button"
       android:text="@string/shr_button_cancel" />

</RelativeLayout>

大功告成!运行应用后,点按每个按钮都会显示水墨涟漪效果。

9dd162d65e4a92a2.gif

5. 转到下一个 fragment

最后,我们将向 LoginFragment.kt 添加一些 Kotlin 代码,以附加要转换到另一个 fragment 的“NEXT”按钮。

让我们在 LoginFragment.kt 中的 onCreateView() 下添加一个私有布尔值 isPasswordValid 方法,并使用逻辑来确定密码是否有效。在本演示中,我们只需确保密码长度至少为 8 个字符:

LoginFragment.kt

private fun isPasswordValid(text: Editable?): Boolean {
   return text != null && text.length >= 8
}

接下来,将点击监听器添加到“Next”按钮,该按钮用于根据我们刚刚创建的 isPasswordValid() 方法设置和清除错误。在 onCreateView() 中,此点击监听器应放置在膨胀器代码行和 return view 代码行之间。

现在,我们来为密码 TextInputEditText 添加一个键监听器,以监听将清除错误的按键事件。此监听器还应使用 isPasswordValid() 检查密码是否有效。您可以直接在 onCreateView() 中的点击监听器下方添加该方法。

您的 onCreateView() 方法现在应如下所示:

LoginFragment.kt

override fun onCreateView(
           inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
       // Inflate the layout for this fragment.
       val view = inflater.inflate(R.layout.shr_login_fragment, container, false)

       // Set an error if the password is less than 8 characters.
       view.next_button.setOnClickListener({
           if (!isPasswordValid(password_edit_text.text!!)) {
               password_text_input.error = getString(R.string.shr_error_password)
           } else {
               // Clear the error.
               password_text_input.error = null
           }
       })

       // Clear the error once more than 8 characters are typed.
       view.password_edit_text.setOnKeyListener({ _, _, _ ->
           if (isPasswordValid(password_edit_text.text!!)) {
               // Clear the error.
               password_text_input.error = null
           }
           false
       })

       return view
   }
}

现在,我们可以导航到另一个 fragment。在 onCreateView() 中,更新 OnClickListener,以在错误验证成功后导航到另一个 fragment。您的 clickListener 代码现在应如下所示:

LoginFragment.kt

// Set an error if the password is less than 8 characters.
view.next_button.setOnClickListener({
   if (!isPasswordValid(password_edit_text.text!!)) {
       password_text_input.error = getString(R.string.shr_error_password)
   } else {
       // Clear the error.
       password_text_input.error = null
       // Navigate to the next Fragment.
       (activity as NavigationHost).navigateTo(ProductGridFragment(), false)
   }
})

我们向点击监听器的 else 情况添加了 (activityas NavigationHost).navigateTo(ProductGridFragment(), false) 代码行。此代码行会从 MainActivity 调用 navigateTo() 方法,以转到新的 fragment(即 ProductGridFragment)。目前,这是一个空白页面,您将在 MDC-102 中完善此页面。

现在,构建应用。接下来,请按“Next”按钮。

大功告成!此屏幕是我们下一个 Codelab 的起点,您将在 MDC-102 中继续使用它。

6. 大功告成

借助基本的 XML 标记和约 30 行 Kotlin 代码,Material Components for Android 库就可以帮助您创建一个符合 Material Design 准则的精美登录页面,同时让该页面在所有设备上保持一致的外观和行为。

后续措施

文本字段和按钮是 MDC Android 库的两个核心组件,但该库还提供了许多其他组件!您可以在 MDC Android 中探索其余组件。或者,前往 MDC 102:Material Design 结构和布局,了解顶部应用栏、卡片视图和网格布局。感谢您试用 Material Components。希望您喜欢此 Codelab!

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

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

我希望日后继续使用 Material Components

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