MDC-102 Android:Material 结构和布局 (Java)

1. 简介

logo_components_color_2x_web_96dp.png

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

在 Codelab MDC-101 中,您使用以下两种 Material Components (MDC) 组件构建了一个登录页面:文本字段和按钮。下面,我们将在此基础上添加导航功能、结构和数据,来扩充此页面。

构建内容

在此 Codelab 中,您将为名为 Shrine 的应用构建一个主屏幕。Shrine 是一款销售服装和家居用品的电子商务应用。其中包含:

  • 顶部应用栏
  • 商品网格列表

249db074eff043f4.png

此 Codelab 中的 MDC-Android 组件

  • AppBarLayout
  • MaterialCardView

所需条件

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

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

新手 中等 熟练

2. 设置您的开发环境

接着 MDC-101 继续操作?

如果您已完成 MDC-101,那么您应该已经准备好用于此 Codelab 的代码。您可以跳至第 3 步:添加顶部应用栏。

从头开始?

下载起始 Codelab 应用

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

…或从 GitHub 克隆

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

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

在 Android Studio 中加载起始代码

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

F5H6srsw_5xOPGFpKrm1RwgewatxA_HUbDI1PWoQUAoJcT6DpfBOkAYwq3S-2vUHvweUaFgAmG7BtUKkGouUbhTwXQh53qec8tO5eVecdlo7QIoLc8rNxFEBb8l7RlS-KzBbZOzVhA

添加项目依赖项

项目需要一个 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 会构建并部署该应用,然后自动在目标设备上将其打开。

大功告成!您应该会看到在 MDC-101 Codelab 中构建的 Shrine 登录页面。

4cb0c218948144b4.png

现在,登录屏幕看起来没有问题,下面我们要在应用中填充一些商品。

3. 添加顶部应用栏

登录页面关闭后,系统会显示主屏幕,其中一个屏幕会显示“You did it!”。太棒了!不过,现在用户没有可执行的操作,也不知道自己在应用中所处的位置。为了解决此问题,我们来添加导航功能。

Material Design 可提供确保高度易用性的导航模式。最重要的导航组件之一是顶部应用栏。

为了提供导航功能并让用户快速执行其他操作,我们来添加一个顶部应用栏。

添加 AppBar widget

shr_product_grid_fragment.xml 中,删除包含“You do it!”TextView<LinearLayout> 标记,并替换成以下代码:

shr_product_grid_fragment.xml

<com.google.android.material.appbar.AppBarLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <androidx.appcompat.widget.Toolbar
       android:id="@+id/app_bar"
       style="@style/Widget.Shrine.Toolbar"
       android:layout_width="match_parent"
       android:layout_height="?attr/actionBarSize"
       app:title="@string/shr_app_name" />
</com.google.android.material.appbar.AppBarLayout>

您的 shr_product_grid_fragment.xml 应如下所示:

shr_product_grid_fragment.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"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".ProductGridFragment">

   <com.google.android.material.appbar.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <androidx.appcompat.widget.Toolbar
           android:id="@+id/app_bar"
           style="@style/Widget.Shrine.Toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           app:title="@string/shr_app_name" />
   </com.google.android.material.appbar.AppBarLayout>
  
</FrameLayout>

许多应用栏的标题旁边都有一个按钮。我们来为该按钮添加一个菜单图标。

添加导航图标

shr_product_grid_fragment.xml 中,将以下内容添加到(您刚刚添加到布局中的)Toolbar XML 组件:

shr_product_grid_fragment.xml

app:navigationIcon="@drawable/shr_menu"

您的 shr_product_grid_fragment.xml 应如下所示:

shr_product_grid_fragment.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"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".ProductGridFragment">
  
   <com.google.android.material.appbar.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <androidx.appcompat.widget.Toolbar
           android:id="@+id/app_bar"
           style="@style/Widget.Shrine.Toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           app:navigationIcon="@drawable/shr_menu"
           app:title="@string/shr_app_name" />
   </com.google.android.material.appbar.AppBarLayout>
  
</FrameLayout>

添加操作按钮并设置顶部应用栏的样式

您也可以在应用栏末尾添加按钮。在 Android 中,这些称为操作按钮

我们将以编程方式设置顶部应用栏的样式并向其菜单添加操作按钮。

首先,我们来创建一个用于设置工具栏的方法。此方法应使用其 id 获取对工具栏的引用,并使用 getActivity() 获取对 activity 的引用。如果 activity 不为 null,请使用 setSupportActionBarToolbar 设置为用作 ActionBar

ProductGridFragment.java

private void setUpToolbar(View view) {
   Toolbar toolbar = view.findViewById(R.id.app_bar);
   AppCompatActivity activity = (AppCompatActivity) getActivity();
   if (activity != null) {
       activity.setSupportActionBar(toolbar);
   }
}

接下来,在我们刚添加的 setUpToolbar 方法的正下方,我们要覆盖 onCreateOptionsMenu,以将 shr_toolbar_menu.xml 的内容膨胀到工具栏中:

ProductGridFragment.java

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
   menuInflater.inflate(R.menu.shr_toolbar_menu, menu);
   super.onCreateOptionsMenu(menu, menuInflater);
}

现在,我们要使用以下代码,为我们向 onCreateView() 方法的内容添加的 setUpToolbar 方法添加一个调用:

ProductGridFragment.java

@Override
public View onCreateView(
       @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   // Inflate the layout for this fragment with the ProductGrid theme
   View view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false);

   // Set up the toolbar
   setUpToolbar(view);

   return view;
}

最后,为 ProductGridFragment.java 添加 onCreate() 方法。在方法正文中,将 setHasOptionMenu 的参数设为 true

该方法应如下所示:

ProductGridFragment.java

@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setHasOptionsMenu(true);
}

以上代码将 XML 布局中的应用栏设为该 activity 的操作栏。回调 onCreateOptionsMenu 会告知 activity 要用作菜单的内容。在本例中,它会将 R.menu.shr_toolbar_menu 中的菜单项放到应用栏中。

该菜单文件包含项内容:“搜索”和“过滤器”。

shr_toolbar_menu.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/search"
       android:icon="@drawable/shr_search"
       android:title="@string/shr_search_title"
       app:showAsAction="always" />
   <item
       android:id="@+id/filter"
       android:icon="@drawable/shr_filter"
       android:title="@string/shr_filter_title"
       app:showAsAction="always" />
</menu>

完成这些更改后,ProductGridFragment.java 文件应如下所示:

ProductGridFragment.java

package com.google.codelabs.mdc.java.shrine;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;


public class ProductGridFragment extends Fragment {

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setHasOptionsMenu(true);
   }
  
   @Override
   public View onCreateView(
           @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       // Inflate the layout for this fragment with the ProductGrid theme
       View view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false);

       // Set up the toolbar
       setUpToolbar(view);

       return view;
   }
  
   private void setUpToolbar(View view) {
       Toolbar toolbar = view.findViewById(R.id.app_bar);
       AppCompatActivity activity = (AppCompatActivity) getActivity();
       if (activity != null) {
           activity.setSupportActionBar(toolbar);
       }
   }

   @Override
   public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
       menuInflater.inflate(R.menu.shr_toolbar_menu, menu);
       super.onCreateOptionsMenu(menu, menuInflater);
   }

}

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

d04e8aa3b27f4754.png

现在,工具栏中会显示一个导航图标、一个标题,并在右侧显示两个操作图标。工具栏还使用细微的阴影来显示高度,这表明高度与内容位于不同的图层。

4. 添加卡片

现在,我们的应用已具有一些结构,接下来,我们需要将内容放到卡片中,以便对内容进行整理。

添加卡片

首先,在顶部应用栏下方添加一张卡片。卡片应包含一个图像区域、一个标题和一个辅助文本的标签。

shr_product_grid_fragment.xml 中,将以下代码添加到 AppBarLayout 下方:

shr_product_grid_fragment.xml

<com.google.android.material.card.MaterialCardView
   android:layout_width="160dp"
   android:layout_height="180dp"
   android:layout_marginBottom="16dp"
   android:layout_marginLeft="16dp"
   android:layout_marginRight="16dp"
   android:layout_marginTop="70dp"
   app:cardBackgroundColor="?attr/colorPrimaryDark"
   app:cardCornerRadius="4dp">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_gravity="bottom"
       android:background="#FFFFFF"
       android:orientation="vertical"
       android:padding="8dp">

       <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:padding="2dp"
           android:text="@string/shr_product_title"
           android:textAppearance="?attr/textAppearanceHeadline6" />

       <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:padding="2dp"
           android:text="@string/shr_product_description"
           android:textAppearance="?attr/textAppearanceBody2" />
   </LinearLayout>
</com.google.android.material.card.MaterialCardView>

构建并运行:

f6184a55ccb5f920.png

在此预览中,您可以看到卡片是从屏幕左侧边缘插入的,该卡片带有圆角和用于表达卡片高度的阴影。整个区域称为“容器”。除了容器本身之外,容器中的所有元素都是可选的。

您可以在容器中添加以下元素:标题文本、缩略图或头像、子标题文本、分隔线,甚至是按钮和图标。例如,我们刚刚创建的卡片在 LinearLayout 中包含两个 TextView(一个用于标题,一个用于辅助文本),并且与卡片底部对齐。

卡片通常以集合的形式和其他卡片一起出现,在此 Codelab 的下一部分中,我们会将它们作为集合放置在网格中。

5. 创建卡片网格

当屏幕上出现多张卡片时,它们就会组成一个或多个集合。网格中的卡片同处一个平面,这意味着它们的静止高度相同(除非被选中或拖动,但我们不会在此 Codelab 中讨论这些)。

设置卡片网格

看看我们为您提供的 shr_product_card.xml 文件:

shr_product_card.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView 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="wrap_content"
   app:cardBackgroundColor="@android:color/white"
   app:cardElevation="2dp"
   app:cardPreventCornerOverlap="true">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical">

       <com.android.volley.toolbox.NetworkImageView
           android:id="@+id/product_image"
           android:layout_width="match_parent"
           android:layout_height="@dimen/shr_product_card_image_height"
           android:background="?attr/colorPrimaryDark"
           android:scaleType="centerCrop" />

       <LinearLayout
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:orientation="vertical"
           android:padding="16dp">

           <TextView
               android:id="@+id/product_title"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="@string/shr_product_title"
               android:textAppearance="?attr/textAppearanceHeadline6" />

           <TextView
               android:id="@+id/product_price"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="@string/shr_product_description"
               android:textAppearance="?attr/textAppearanceBody2" />
       </LinearLayout>
   </LinearLayout>
</com.google.android.material.card.MaterialCardView>

此卡片布局包含一个卡片,该卡片包含一个图片(这里是 NetworkImageView,它允许包含来自网址的图片)和两个 TextViews

接下来,看看我们为您提供的 ProductCardRecyclerViewAdapter。它与 ProductGridFragment 位于同一软件包中。

ProductCardRecyclerViewAdapter.java

package com.google.codelabs.mdc.java.shrine;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.google.codelabs.mdc.java.shrine.network.ImageRequester;
import com.google.codelabs.mdc.java.shrine.network.ProductEntry;

import java.util.List;

/**
* Adapter used to show a simple grid of products.
*/
public class ProductCardRecyclerViewAdapter extends RecyclerView.Adapter<ProductCardViewHolder> {

   private List<ProductEntry> productList;
   private ImageRequester imageRequester;

   ProductCardRecyclerViewAdapter(List<ProductEntry> productList) {
       this.productList = productList;
       imageRequester = ImageRequester.getInstance();
   }

   @NonNull
   @Override
   public ProductCardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
       View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.shr_product_card, parent, false);
       return new ProductCardViewHolder(layoutView);
   }

   @Override
   public void onBindViewHolder(@NonNull ProductCardViewHolder holder, int position) {
       // TODO: Put ViewHolder binding code here in MDC-102
   }

   @Override
   public int getItemCount() {
       return productList.size();
   }
}

上方的适配器类负责管理网格的内容。为了确定每个视图应对其给定内容执行的操作,我们即将为 onBindViewHolder() 编写代码。

在同一软件包中,您还可以查看 ProductCardViewHolder。此类用于存储可影响卡片布局的视图,以便稍后进行修改。

ProductCardViewHolder.java

package com.google.codelabs.mdc.java.shrine;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;

public class ProductCardViewHolder extends RecyclerView.ViewHolder {

   public ProductCardViewHolder(@NonNull View itemView) {
       super(itemView);
       // TODO: Find and store views from itemView
   }
}

为了设置网格,我们首先要从 shr_product_grid_fragment.xml 中移除占位符 MaterialCardView。接下来,您应添加代表卡片网格的组件。在本例中,请将 RecyclerView 组件添加到 shr_product_grid_fragment.xml 中的 AppBarLayout XML 组件下方:

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:paddingStart="@dimen/shr_product_grid_spacing"
   android:paddingEnd="@dimen/shr_product_grid_spacing"
   app:layout_behavior="@string/appbar_scrolling_view_behavior">

   <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/recycler_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />

</androidx.core.widget.NestedScrollView>

您的 shr_product_grid_fragment.xml 应如下所示:

shr_product_grid_fragment.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"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".ProductGridFragment">

   <com.google.android.material.appbar.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <androidx.appcompat.widget.Toolbar
           android:id="@+id/app_bar"
           style="@style/Widget.Shrine.Toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           app:navigationIcon="@drawable/shr_menu"
           app:title="@string/shr_app_name" />
   </com.google.android.material.appbar.AppBarLayout>

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

       <androidx.recyclerview.widget.RecyclerView
           android:id="@+id/recycler_view"
           android:layout_width="match_parent"
           android:layout_height="match_parent" />

   </androidx.core.widget.NestedScrollView>

</FrameLayout>

最后,在 ProductGridFragment.java 中,将 RecyclerView 初始化代码添加到 onCreateView() 中,就放在调用 setUpToolbar(view) 之后,且位于 return 语句之前:

ProductGridFragment.java

@Override
public View onCreateView(
       @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   ...
   setUpToolbar(view);

   // Set up the RecyclerView
   RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
   recyclerView.setHasFixedSize(true);
   recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2, GridLayoutManager.VERTICAL, false));
   ProductCardRecyclerViewAdapter adapter = new ProductCardRecyclerViewAdapter(
           ProductEntry.initProductEntryList(getResources()));
   recyclerView.setAdapter(adapter);
   int largePadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing);
   int smallPadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small);
   recyclerView.addItemDecoration(new ProductGridItemDecoration(largePadding, smallPadding));

   return view;
}

上述代码段包含设置 RecyclerView 所必需的初始化步骤,其中包括设置 RecyclerView 的布局管理器,以及初始化和设置 RecyclerView 的适配器。

您的 ProductGridFragment.java 文件现在应如下所示:

ProductGridFragment.java

package com.google.codelabs.mdc.java.shrine;

import android.os.Bundle;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;


import com.google.codelabs.mdc.java.shrine.network.ProductEntry;

public class ProductGridFragment extends Fragment {

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setHasOptionsMenu(true);
   }

   @Override
   public View onCreateView(
           @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       // Inflate the layout for this fragment with the ProductGrid theme
       View view = inflater.inflate(R.layout.shr_product_grid_fragment, container, false);

       // Set up the toolbar
       setUpToolbar(view);

       // Set up the RecyclerView
       RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
       recyclerView.setHasFixedSize(true);
       recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2, GridLayoutManager.VERTICAL, false));
       ProductCardRecyclerViewAdapter adapter = new ProductCardRecyclerViewAdapter(
               ProductEntry.initProductEntryList(getResources()));
       recyclerView.setAdapter(adapter);
       int largePadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing);
       int smallPadding = getResources().getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small);
       recyclerView.addItemDecoration(new ProductGridItemDecoration(largePadding, smallPadding));

       return view;
   }

   private void setUpToolbar(View view) {
       Toolbar toolbar = view.findViewById(R.id.app_bar);
       AppCompatActivity activity = (AppCompatActivity) getActivity();
       if (activity != null) {
           activity.setSupportActionBar(toolbar);
       }
   }

   @Override
   public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
       menuInflater.inflate(R.menu.shr_toolbar_menu, menu);
       super.onCreateOptionsMenu(menu, menuInflater);
   }

}

构建并运行。

f9aeab846fc3bb4c.png

卡片现在出现在屏幕中了!卡片还未显示任何内容,让我们来添加一些商品数据。

添加图片和文字

为每张卡片添加图片、商品名称和价格。我们的 ViewHolder 抽象会存储每张卡片的视图。在我们的 ViewHolder 中,添加三个视图,如下所示:

ProductCardViewHolder.java

package com.google.codelabs.mdc.java.shrine;

import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;

import com.android.volley.toolbox.NetworkImageView;

public class ProductCardViewHolder extends RecyclerView.ViewHolder {

   public NetworkImageView productImage;
   public TextView productTitle;
   public TextView productPrice;

   public ProductCardViewHolder(@NonNull View itemView) {
       super(itemView);
       productImage = itemView.findViewById(R.id.product_image);
       productTitle = itemView.findViewById(R.id.product_title);
       productPrice = itemView.findViewById(R.id.product_price);
   }
}

在我们的 RecyclerView 的适配器中,在 ViewHolder, 中更新 onBindViewHolder() 方法,以设置每个视图中的信息:

ProductCardRecyclerViewAdapter.java

@Override
public void onBindViewHolder(@NonNull ProductCardViewHolder holder, int position) {
   if (productList != null && position < productList.size()) {
       ProductEntry product = productList.get(position);
       holder.productTitle.setText(product.title);
       holder.productPrice.setText(product.price);
       imageRequester.setImageFromUrl(holder.productImage, product.url);
   }
}

上面的代码使用 ViewHolder 告知 RecyclerView 的适配器如何处理每张卡片。

在这里,它会设置 ViewHolder 中每个 TextView 的文本数据,并调用 ImageRequester ,以获取来自网址的图片。ImageRequester 是我们为方便您而提供的一个类,它使用 Volley 库(这不是此 Codelab 讨论的主题,但您可以自行探索该代码)。

构建并运行:

249db074eff043f4

我们的商品现在出现在应用中了!

6. 回顾

我们的应用已经有了基本的流程,可将用户从登录页面转到主屏幕,然后用户可在主屏幕中查看商品。通过几行代码,我们添加了一个顶部应用栏(包含标题和三个按钮),以及一个用于展示应用内容的卡片网格。现在,我们的主屏幕非常简单实用,具有基本的结构和可操作的内容。

后续步骤

您现在使用了 MDC-Android 库中的四个 Material Design 核心组件:顶部应用栏、卡片、文本字段和按钮!您可以在 MDC Android 的 MDC-Android Catalog 组件中探索更多组件。

尽管我们的应用具有完善的功能,但尚未表达任何特定的品牌。在 MDC-103:通过颜色、形状、高度和类型设置 Material Design 主题中,我们将自定义这些组件的样式,来诠释一个充满活力的、现代的品牌。

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

非常赞同 赞同 中立 不赞同 非常不赞同

我希望日后继续使用 Material Components

非常同意 同意 中立 不同意 非常不同意