MDC-102 Android: Cấu trúc và bố cục Material (Java)

1. Giới thiệu

logo_components_color_2x_web_96dp.png

Thành phần Material (MDC) giúp nhà phát triển triển khai Material Design. Được tạo bởi một nhóm các kỹ sư và nhà thiết kế trải nghiệm người dùng tại Google, MDC có hàng chục thành phần giao diện người dùng đẹp mắt, dễ sử dụng và được cung cấp cho Android, iOS, web và Flutter.material.io/develop

Trong lớp học lập trình MDC-101, bạn đã sử dụng 2 Thành phần Material (MDC) để xây dựng trang đăng nhập: các trường văn bản và nút. Bây giờ, hãy mở rộng nền tảng này bằng cách thêm thành phần điều hướng, cấu trúc và dữ liệu.

Sản phẩm bạn sẽ tạo ra

Trong lớp học lập trình này, bạn sẽ xây dựng màn hình chính cho một ứng dụng có tên là Shrine. Đây là một ứng dụng thương mại điện tử bán quần áo và đồ gia dụng. Bản tóm tắt sẽ chứa:

  • Thanh ứng dụng trên cùng
  • Danh sách sản phẩm ở dạng lưới

249db074eff043f4.png

Các thành phần MDC-Android trong lớp học lập trình này

  • AppBarLayout
  • MaterialCardView

Bạn cần có

  • Kiến thức cơ bản về phát triển Android
  • Android Studio (tải xuống tại đây nếu bạn chưa có)
  • Trình mô phỏng hoặc thiết bị Android (có trên Android Studio)
  • Mã mẫu (xem bước tiếp theo)

Bạn đánh giá thế nào về mức độ kinh nghiệm của mình trong việc xây dựng ứng dụng Android?

Người mới làm quen Trung cấp Thành thạo

2. Thiết lập môi trường phát triển

Tiếp tục từ MDC-101?

Nếu bạn đã hoàn thành MDC-101, thì mã của bạn đã sẵn sàng cho lớp học lập trình này. Bạn có thể chuyển sang bước 3: Thêm thanh ứng dụng trên cùng.

Bạn muốn bắt đầu từ đầu?

Tải ứng dụng ban đầu của lớp học lập trình xuống

Ứng dụng khởi động nằm trong thư mục material-components-android-codelabs-102-starter/java. Hãy nhớ cd vào thư mục đó trước khi bắt đầu.

...hoặc sao chép từ GitHub

Để sao chép lớp học lập trình này từ GitHub, hãy chạy các lệnh sau:

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

Tải mã khởi đầu trong Android Studio

  1. Sau khi trình hướng dẫn thiết lập hoàn tất và cửa sổ Welcome to Android Studio (Chào mừng bạn đến với Android Studio) hiển thị, hãy nhấp vào Open an existing Android Studio project (Mở một dự án Android Studio hiện có). Chuyển đến thư mục mà bạn đã cài đặt mã mẫu rồi chọn java -> shrine (hoặc tìm shrine trên máy tính) để mở dự án Shrine.
  2. Đợi một chút để Android Studio tạo và đồng bộ hoá dự án, như được hiển thị bằng các chỉ báo hoạt động ở cuối cửa sổ Android Studio.
  3. Tại thời điểm này, Android Studio có thể phát sinh một số lỗi bản dựng do bạn đang thiếu SDK Android hoặc công cụ xây dựng, chẳng hạn như lỗi hiển thị dưới đây. Làm theo hướng dẫn trong Android Studio để cài đặt/cập nhật các ứng dụng này và đồng bộ hoá dự án của bạn.

F5H6srsw_5xOPGFpKrm1RwgewatxA_HUbDI1PWoQUAoJcT6DpfBOkAYwq3S-2vUHvweUaFgAmG7BtUKkGouUbhTwXQh53qec8tO5eVecdlo7QIoLc8rNxFEBb8l7RlS-KzBbZOzVhA

Thêm phần phụ thuộc của dự án

Dự án cần có phần phụ thuộc trên thư viện hỗ trợ MDC Android. Mã mẫu bạn tải xuống đã liệt kê phần phụ thuộc này, nhưng bạn nên thực hiện các bước sau đây để đảm bảo phần phụ thuộc này.

  1. Chuyển đến tệp build.gradle của mô-đun app và đảm bảo rằng khối dependencies bao gồm một phần phụ thuộc trên MDC Android:
api 'com.google.android.material:material:1.1.0-alpha06'
  1. (Không bắt buộc) Nếu cần, hãy chỉnh sửa tệp build.gradle để thêm các phần phụ thuộc sau và đồng bộ hoá dự án.
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'
}

Chạy ứng dụng ban đầu

  1. Đảm bảo cấu hình bản dựng ở bên trái nút Chạy/Phátapp.
  2. Nhấn nút Chạy / Chạy màu xanh lục để tạo và chạy ứng dụng.
  3. Trong cửa sổ Select Deployment Target (Chọn đối tượng triển khai), nếu bạn đã có thiết bị Android trong danh sách thiết bị hiện có, hãy chuyển đến Bước 8. Nếu không, hãy nhấp vào Create New Virtual Device (Tạo thiết bị ảo mới).
  4. Trên màn hình Select Hardware (Chọn phần cứng), hãy chọn một thiết bị điện thoại, chẳng hạn như Pixel 2, sau đó nhấp vào Next (Tiếp theo).
  5. Trong màn hình System Image (Hình ảnh hệ thống), hãy chọn một phiên bản Android gần đây, tốt nhất là cấp độ API cao nhất. Nếu chưa cài đặt, hãy nhấp vào đường liên kết Tải xuống xuất hiện rồi hoàn tất quá trình tải xuống.
  6. Nhấp vào Tiếp theo.
  7. Trên màn hình Thiết bị Android ảo (AVD), hãy giữ nguyên chế độ cài đặt và nhấp vào Hoàn tất.
  8. Chọn một thiết bị Android trong hộp thoại mục tiêu triển khai.
  9. Nhấp vào Ok.
  10. Android Studio sẽ tạo ứng dụng, triển khai ứng dụng và tự động mở ứng dụng trên thiết bị mục tiêu.

Thành công! Bạn sẽ thấy trang đăng nhập Đền trong lớp học lập trình MDC-101.

4cb0c218948144b4.png

Giờ đây, khi màn hình đăng nhập đã ổn, hãy điền một số sản phẩm vào ứng dụng.

3. Thêm thanh ứng dụng trên cùng

Màn hình chính sẽ xuất hiện khi trang đăng nhập đóng lại, với màn hình cho biết "Bạn đã làm được!". Thật tuyệt! Nhưng giờ đây, người dùng không cần làm gì hoặc không biết vị trí của họ trong ứng dụng. Để làm được điều đó, hãy thêm tính năng điều hướng.

Material Design cung cấp các mẫu điều hướng đảm bảo khả năng hữu dụng ở mức cao. Một trong những thành phần điều hướng đáng chú ý nhất là thanh ứng dụng trên cùng.

Để cung cấp tính năng điều hướng và cho phép người dùng truy cập nhanh vào các thao tác khác, hãy thêm một thanh ứng dụng ở trên cùng.

Thêm tiện ích AppBar

Trong shr_product_grid_fragment.xml, hãy xoá thẻ <LinearLayout> có chứa dòng chữ "Bạn đã làm được!" TextView và thay thế bằng nội dung sau:

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 của bạn sẽ có dạng như sau:

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>

Nhiều thanh ứng dụng có một nút bên cạnh tiêu đề. Hãy thêm biểu tượng trình đơn vào ứng dụng của chúng ta.

Thêm biểu tượng điều hướng

Khi vẫn ở trong shr_product_grid_fragment.xml, hãy thêm đoạn mã sau vào thành phần XML Toolbar (bạn vừa thêm thành phần này vào bố cục):

shr_product_grid_fragment.xml

app:navigationIcon="@drawable/shr_menu"

shr_product_grid_fragment.xml của bạn sẽ có dạng như sau:

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>

Thêm các nút hành động và tạo kiểu cho thanh ứng dụng trên cùng

Bạn cũng có thể thêm các nút vào phía cuối của thanh ứng dụng. Trong Android, đây được gọi là nút hành động.

Chúng ta sẽ tạo kiểu cho thanh ứng dụng trên cùng và thêm các nút hành động vào trình đơn của thanh ứng dụng đó theo phương thức lập trình.

Trước tiên, hãy tạo một phương thức để thiết lập thanh công cụ. Phương thức này phải tham chiếu đến thanh công cụ bằng id và cũng tham chiếu đến hoạt động bằng getActivity(). Nếu hoạt động không phải là giá trị rỗng, hãy đặt Toolbar để sử dụng làm ActionBar bằng setSupportActionBar:

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

Tiếp theo, ngay bên dưới phương thức setUpToolbar mà chúng ta vừa thêm, hãy ghi đè onCreateOptionsMenu để tăng cường nội dung của shr_toolbar_menu.xml vào thanh công cụ:

ProductGridFragment.java

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

Bây giờ, hãy thêm lệnh gọi đến phương thức setUpToolbar mà chúng ta đã thêm vào nội dung của phương thức onCreateView() bằng cách sau:

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

Cuối cùng, hãy thêm phương thức onCreate() vào ProductGridFragment.java. Trong phần nội dung của phương thức, hãy đặt tham số của setHasOptionMenu thành true.

Phương thức sẽ có dạng như sau:

ProductGridFragment.java

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

Đoạn mã trên đặt thanh ứng dụng trong bố cục XML của chúng ta thành Thanh thao tác cho hoạt động này. Lệnh gọi lại onCreateOptionsMenu cho hoạt động biết cần sử dụng trình đơn nào. Trong trường hợp này, thao tác này sẽ đưa các mục trong trình đơn từ R.menu.shr_toolbar_menu vào thanh ứng dụng.

Tệp trình đơn chứa hai mục: "Tìm kiếm" và "Lọc".

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>

Sau khi thực hiện những thay đổi đó, tệp ProductGridFragment.java của bạn sẽ có dạng như sau:

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

}

Tạo bản dựng và chạy. Màn hình chính của bạn sẽ có dạng như sau:

d04e8aa3b27f4754.png

Giờ đây, thanh công cụ đã có biểu tượng điều hướng, tiêu đề và hai biểu tượng hành động ở bên phải. Thanh công cụ cũng hiển thị độ cao bằng cách sử dụng bóng mờ, cho biết độ cao nằm trên một lớp khác với nội dung.

4. Thêm thẻ

Ứng dụng của chúng ta hiện đã có cấu trúc nhất định, hãy sắp xếp nội dung bằng cách đưa nội dung vào các thẻ.

Thêm thẻ

Hãy bắt đầu bằng cách thêm một thẻ bên dưới thanh ứng dụng trên cùng. Thẻ phải có một vùng cho hình ảnh, tiêu đề và nhãn cho văn bản phụ.

Trong shr_product_grid_fragment.xml, hãy thêm nội dung sau vào bên dưới 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>

Tạo và chạy:

f6184a55ccb5f920.png

Trong bản xem trước này, bạn có thể thấy thẻ được lồng ghép từ cạnh trái của màn hình, thẻ có các góc bo tròn và bóng đổ (biểu thị độ cao của thẻ). Toàn bộ khu vực này được gọi là "vùng chứa". Ngoài vùng chứa, bạn có thể cài đặt tất cả các phần tử trong vùng chứa đó.

Bạn có thể thêm các thành phần sau đây vào một vùng chứa: văn bản tiêu đề, hình thu nhỏ hoặc hình đại diện, văn bản tiêu đề phụ, dấu phân chia, cũng như các nút và biểu tượng. Ví dụ: thẻ chúng ta vừa tạo chứa hai TextView (một cho tiêu đề và một cho văn bản phụ) trong LinearLayout, được căn chỉnh ở cuối thẻ.

Thẻ thường xuất hiện trong một bộ sưu tập cùng với các thẻ khác. Trong phần tiếp theo của lớp học lập trình này, chúng ta sẽ sắp xếp các thành phần này dưới dạng một tập hợp trong lưới.

5. Tạo một lưới thẻ

Khi có nhiều thẻ trên một màn hình, các thẻ đó sẽ được nhóm lại với nhau thành một hoặc nhiều bộ sưu tập. Các thẻ trong lưới là đồng phẳng, nghĩa là các thẻ này có cùng cao độ nghỉ ngơi với nhau (trừ phi được chọn hoặc kéo, nhưng chúng ta sẽ không đề cập đến điều đó trong lớp học lập trình này).

Thiết lập lưới thẻ

Hãy xem tệp shr_product_card.xml mà chúng tôi đã cung cấp cho bạn:

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>

Bố cục thẻ này chứa một thẻ có hình ảnh (ở đây là NetworkImageView, cho phép chúng ta thêm hình ảnh từ một URL) và hai TextViews.

Tiếp theo, hãy xem ProductCardRecyclerViewAdapter mà chúng tôi đã cung cấp cho bạn. Lớp này nằm trong cùng một gói với 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();
   }
}

Lớp bộ chuyển đổi ở trên quản lý nội dung lưới. Để xác định việc mỗi thành phần hiển thị sẽ làm gì với nội dung nhất định, chúng ta sẽ sớm viết mã cho onBindViewHolder().

Trong cùng một gói, bạn cũng có thể xem ProductCardViewHolder. Lớp này lưu trữ những chế độ xem ảnh hưởng đến bố cục thẻ để chúng ta có thể sửa đổi sau.

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

Để thiết lập lưới, trước tiên, chúng ta cần xoá phần giữ chỗ MaterialCardView khỏi shr_product_grid_fragment.xml. Tiếp theo, bạn nên thêm thành phần đại diện cho lưới thẻ. Trong trường hợp này, hãy thêm một thành phần RecyclerView vào shr_product_grid_fragment.xml bên dưới thành phần XML AppBarLayout:

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 của bạn sẽ có dạng như sau:

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>

Cuối cùng, trong onCreateView(), hãy thêm mã khởi chạy RecyclerView vào ProductGridFragment.java sau khi bạn gọi setUpToolbar(view) và trước câu lệnh 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;
}

Đoạn mã ở trên chứa các bước khởi chạy cần thiết để thiết lập RecyclerView. Điều này bao gồm việc thiết lập trình quản lý bố cục của RecyclerView, đồng thời khởi chạy và thiết lập trình chuyển đổi của RecyclerView.

Bây giờ, tệp ProductGridFragment.java của bạn sẽ có dạng như sau:

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

}

Tạo bản dựng và chạy.

f9aeab846fc3bb4c.png

Các thẻ đã xuất hiện! Chúng chưa cho thấy bất cứ thông tin nào, vì vậy, hãy thêm một số dữ liệu sản phẩm.

Thêm hình ảnh và văn bản

Thêm hình ảnh, tên sản phẩm và giá cho mỗi thẻ. Tính năng trừu tượng ViewHolder của chúng ta lưu giữ các thành phần hiển thị cho mỗi thẻ. Trong ViewHolder, hãy thêm 3 thành phần hiển thị như sau:

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

Trong trình chuyển đổi của RecyclerView, trong ViewHolder,, hãy cập nhật phương thức onBindViewHolder() để đặt thông tin trên mỗi thành phần hiển thị:

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

Đoạn mã ở trên cho trình chuyển đổi của RecyclerView biết việc cần làm với mỗi thẻ bằng cách sử dụng ViewHolder.

Tại đây, lớp này đặt dữ liệu văn bản trên mỗi TextView của ViewHolder và gọi ImageRequester để lấy hình ảnh từ một URL. ImageRequester là một lớp chúng tôi cung cấp để thuận tiện cho bạn và sử dụng thư viện Volley (Đó là một chủ đề không thuộc phạm vi của lớp học lập trình này, nhưng bạn có thể tự khám phá mã nguồn này).

Tạo và chạy:

249db074eff043f4.png

Sản phẩm của chúng tôi đang xuất hiện trong ứng dụng!

6. Tóm tắt

Ứng dụng của chúng ta có một quy trình cơ bản đưa người dùng từ màn hình đăng nhập đến màn hình chính, nơi họ có thể xem sản phẩm. Chỉ với vài dòng mã, chúng ta đã thêm một thanh ứng dụng trên cùng có tiêu đề và 3 nút, cùng một lưới thẻ để trình bày nội dung của ứng dụng. Màn hình chính của chúng ta hiện đã đơn giản và dễ sử dụng với cấu trúc cơ bản và nội dung dễ thao tác.

Các bước tiếp theo

Với thanh ứng dụng, thẻ, trường văn bản và nút ở trên cùng, chúng ta hiện đã sử dụng 4 thành phần Material Design cốt lõi trong thư viện MDC-Android! Bạn có thể khám phá nhiều thành phần hơn trong thành phần của MDC Android trong Danh mục MDC-Android.

Mặc dù có đầy đủ chức năng, nhưng ứng dụng của chúng tôi chưa cho thấy bất kỳ thương hiệu cụ thể nào. Trong phần MDC-103: Tuỳ chỉnh giao diện Material Design bằng màu sắc, hình dạng, độ cao và kiểu, chúng ta sẽ tuỳ chỉnh kiểu của các thành phần này để thể hiện một thương hiệu sống động, hiện đại.

Tôi có thể hoàn thành lớp học lập trình này với thời gian và công sức hợp lý

Hoàn toàn đồng ý Đồng ý Trung lập Không đồng ý Hoàn toàn không đồng ý

Tôi muốn tiếp tục sử dụng Thành phần Material trong tương lai

Hoàn toàn đồng ý Đồng ý Bình thường Không đồng ý Hoàn toàn không đồng ý