MDC-102 Android:Material Design 結構和版面配置 (Java)

1. 簡介

logo_components_color_2x_web_96dp.png

Material 元件 (MDC) 可協助開發人員實作 Material Design。MDC 是由 Google 的工程師和使用者體驗設計師團隊所開發,提供數十種美觀實用的 UI 元件,適用於 Android、iOS、網頁和 Flutter。material.io/develop

在程式碼研究室 MDC-101 中,您使用兩個質感設計元件 (MDC) 建構登入頁面:文字欄位和按鈕。現在,讓我們加入導覽、結構和資料,進一步擴充這個基礎。

建構項目

在本程式碼研究室中,您會為名為 Shrine 的電子商務應用程式建構主畫面,這是一款用於販售服飾和居家用品的電子商務應用程式。其中包含:

  • 頂端應用程式列
  • 產品的格狀清單

249db074eff043f4.png

本程式碼研究室中的 MDC-Android 元件

  • AppBarLayout
  • MaterialCardView

軟硬體需求

  • 具備 Android 開發作業的基本知識
  • Android Studio (如果尚未安裝,請按這裡下載)
  • Android 模擬器或裝置 (可透過 Android Studio 取得)
  • 程式碼範例 (請參閱下一步)

請評估您建構 Android 應用程式的經驗程度。

初級 中級 適合

2. 設定開發環境

是否要從 MDC-101 課程繼續學習?

如果您已完成 MDC-101 課程,您的程式碼應已準備好用於本程式碼研究室。您可以略過步驟 3,直接進行「新增頂端應用程式列」步驟。

從頭開始

下載範例程式碼研究室應用程式

範例應用程式位於 material-components-android-codelabs-102-starter/java 目錄中。請務必先 cd 放入該目錄,再開始操作。

...或從 GitHub 複製

如要從 GitHub 複製本程式碼研究室,請執行下列指令:

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」->「shrine」(或在電腦上搜尋「shrine」),開啟 Shrine 專案。
  2. 請稍候,讓 Android Studio 建構及同步處理專案,Android Studio 視窗底部的活動指標會顯示這項作業的進度。
  3. 此時,Android Studio 可能會引發部分建構錯誤,因為缺少 Android SDK 或建構工具 (如下所示)。請按照 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」按鈕左側的建構設定為 app
  2. 按下綠色的「Run/Play」按鈕,即可建構並執行應用程式。
  3. 如果「Select Deployment Target」視窗中已列出 Android 裝置,請直接跳至步驟 8。否則,請按一下「Create New Virtual Device」
  4. 在「選取硬體」畫面中選取手機裝置 (例如「Pixel 2」),然後點選「下一步」
  5. 在「System Image」畫面中選取最新的 Android 版本 (最好是最高 API 級別)。如果尚未安裝,請按一下畫面上的「Download」連結,完成下載程序。
  6. 點選「下一步」
  7. 在「Android Virtual Device (AVD)」(Android 虛擬裝置 (AVD)) 畫面上維持原設定,然後按一下「Finish」(完成)
  8. 從部署目標對話方塊中選取「Android 裝置」
  9. 按一下 [確定]。
  10. Android Studio 會建構及部署應用程式,並自動在目標裝置上開啟應用程式。

大功告成!您應該會在 MDC-101 程式碼研究室中看到 Shrine 登入頁面。

4cb0c218948144b4.png

現在登入畫面沒有問題,讓我們在應用程式中填入部分產品。

3. 新增頂端應用程式列

登入頁面關閉後,主畫面會顯示「你成功了!」畫面。太好了!不過,使用者現在無法採取任何行動,也無法瞭解自己在應用程式中的位置。為此,我們來新增導覽功能。

Material Design 提供的導覽模式可確保高度可用性。頂端應用程式列是最顯著的導覽元件之一。

如要提供導覽選項,並讓使用者快速存取其他動作,請加入頂端應用程式列。

新增 AppBar 小工具

shr_product_grid_fragment.xml 中,刪除包含「您做到了!」的 <LinearLayout> 標記TextView 並替換為以下內容:

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() 取得活動的參照。如果活動不是空值,請使用 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);
}

現在,新增對 setUpToolbar 方法的呼叫 (我們加到 onCreateView() 方法的內容中),如下所示:

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;
}

最後,將 onCreate() 方法新增至 ProductGridFragment.java。在方法主體中,將 setHasOptionMenu 的參數設為 true

方法應如下所示:

ProductGridFragment.java

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

上述程式碼會將 XML 版面配置中的應用程式列設為此活動的動作列。回呼 onCreateOptionsMenu 會指示活動要使用哪個項目做為選單。在這種情況下,它會將 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.xmlAppBarLayout 下方,新增以下內容:

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 (一個用於標題,另一個用於次要文字),一個與資訊卡底部對齊。

資訊卡通常會與其他資訊卡一起顯示。在本程式碼研究室的下一節中,我們會以格線的形式列出這些元件。

5. 建立卡片格狀

如果畫面中有多張資訊卡,系統會將這些資訊卡分組成一個或多個集合。格狀卡片是共平面,也就是說,它們會共用相同的靜止高度 (除非選取或拖曳,但我們不會在本程式碼研究室中介紹這項操作)。

設定卡片網格

查看我們提供給您的 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。接下來,您應該新增代表資訊卡格線的元件。在此情況下,請在 AppBarLayout XML 元件下方的 shr_product_grid_fragment.xml 中新增 RecyclerView 元件:

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>

最後,在 onCreateView() 中,在呼叫 setUpToolbar(view) 後、return 陳述式前,將 RecyclerView 初始化程式碼新增至 ProductGridFragment.java

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);
   }
}

上述程式碼會指示 RecyclerView 的轉接器使用 ViewHolder 處理每張卡片的操作。

這個範例會設定 ViewHolder 每個 TextView 上的文字資料,並呼叫 ImageRequester 從網址取得圖片。ImageRequester 是我們為了方便您使用而提供的類別,並使用 Volley 程式庫 (這項主題不在本程式碼研究室的範圍內,但歡迎您自行探索程式碼)。

建構並執行:

249db074eff043f4.png

我們的產品現在可顯示在應用程式中!

6. 重點回顧

我們的應用程式具有基本流程,可將使用者從登入畫面帶往可查看產品的主畫面。我們只需加入幾行程式碼,就能新增頂端應用程式列 (含標題和三個按鈕),以及資訊卡格狀陣列,用於呈現應用程式內容。我們的首頁現在簡單實用,具備基本結構和可執行內容。

後續步驟

有了頂端應用程式列、資訊卡、文字欄位和按鈕,我們現在已使用 MDC-Android 程式庫中的四個核心 Material Design 元件!您還可以在 MDC-Android Catalog 的 MDC Android 元件中探索更多元件。

雖然功能完整,但應用程式無法呈現任何特定品牌。在 MDC-103:利用顏色、形狀、高度和類型建立 Material Design 主題 中,我們將自訂這些元件的樣式,以呈現鮮明、現代的品牌形象。

我能夠在合理的時間和精力下完成本程式碼研究室

非常同意 同意 普通 不同意 非常不同意

我想日後繼續使用 Material Design 元件

非常同意 同意 無意見 不同意 非常不同意