MDC-102 Android: โครงสร้างและเลย์เอาต์ของวัสดุ (Kotlin)

1. บทนำ

logo_components_color_2x_web_96dp.png

Material Components (MDC) ช่วยให้นักพัฒนาแอปนำ Material Design ไปใช้ได้ MDC สร้างขึ้นโดยทีมวิศวกรและนักออกแบบ UX ของ Google โดยมีคอมโพเนนต์ UI ที่สวยงามและใช้งานได้จริงมากมาย และพร้อมใช้งานสำหรับ Android, iOS, เว็บ และ Flutter.material.io/develop

ใน Codelab MDC-101 คุณได้ใช้ Material Components (MDC) 2 รายการเพื่อสร้างหน้าเข้าสู่ระบบ ได้แก่ ช่องข้อความและปุ่มที่มีการกระเพื่อมของหมึก ตอนนี้เรามาขยายฐานรากนี้ด้วยการเพิ่มการนำทาง โครงสร้าง และข้อมูล

สิ่งที่คุณจะได้สร้าง

ใน Codelab นี้ คุณจะได้สร้างหน้าจอหลักสำหรับแอปที่ชื่อว่า Shrine ซึ่งเป็นแอปอีคอมเมิร์ซที่ขายเสื้อผ้าและของใช้ในบ้าน โดยจะมีองค์ประกอบต่อไปนี้

  • แถบแอปด้านบน
  • รายการแบบตารางที่เต็มไปด้วยผลิตภัณฑ์

249db074eff043f4.png

คอมโพเนนต์ MDC-Android ใน Codelab นี้

  • AppBarLayout
  • MaterialCardView

สิ่งที่คุณต้องมี

  • ความรู้พื้นฐานเกี่ยวกับการพัฒนาแอป Android
  • Android Studio (ดาวน์โหลด ที่นี่ หากยังไม่มี)
  • โปรแกรมจำลองหรืออุปกรณ์ Android (พร้อมใช้งานผ่าน Android Studio)
  • โค้ดตัวอย่าง (ดูขั้นตอนถัดไป)

คุณให้คะแนนประสบการณ์การสร้างแอป Android ของคุณอยู่ในระดับใด

ระดับเริ่มต้น ระดับกลาง ระดับสูง

2. ตั้งค่าสภาพแวดล้อมในการพัฒนาซอฟต์แวร์

ทำต่อจาก MDC-101 ใช่ไหม

หากทำ MDC-101 เสร็จแล้ว โค้ดของคุณควรพร้อมสำหรับ Codelab นี้ ให้ข้ามไปที่ขั้นตอนที่ 3: เพิ่มแถบแอปด้านบน

เริ่มต้นจากศูนย์ใช่ไหม

ดาวน์โหลดแอป Codelab สำหรับผู้เริ่มต้น

แอปเริ่มต้นจะอยู่ในไดเรกทอรี material-components-android-codelabs-102-starter/kotlin อย่าลืมใช้คำสั่ง cd เพื่อไปยังไดเรกทอรีดังกล่าว ก่อนเริ่มต้น

...หรือโคลนจาก GitHub

หากต้องการโคลน Codelab นี้จาก 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 ไปที่ไดเรกทอรีที่คุณติดตั้งโค้ดตัวอย่างไว้ แล้วเลือก kotlin -> shrine (หรือค้นหา shrine ในคอมพิวเตอร์) เพื่อเปิดโปรเจ็กต์ Shipping
  2. รอสักครู่เพื่อให้ Android Studio สร้างและซิงค์โปรเจ็กต์ ดังที่แสดงโดยตัวบ่งชี้กิจกรรมที่ด้านล่างของหน้าต่าง Android Studio
  3. ในขั้นตอนนี้ Android Studio อาจแสดงข้อผิดพลาดในการสร้างบางอย่างเนื่องจากคุณไม่มี Android SDK หรือเครื่องมือสร้าง เช่น ข้อผิดพลาดที่แสดงด้านล่าง ทำตามวิธีการใน Android Studio เพื่อติดตั้ง/อัปเดต SDK และเครื่องมือเหล่านี้ แล้วซิงค์ โปรเจ็กต์

KzoYWC1S7Se7yL8igi1vXF_mbVxAdl2lg5kb7RODrsVpEng0G6U3NK1Qnn0faBBZd2u71yMXioy9tD-7fv3NXvVO4N3EtMMeWDTmqBMMl6egd9R5uXX0T_SKmahbmRor3wZZHX0ByA

เพิ่มทรัพยากร Dependency ของโปรเจ็กต์

โปรเจ็กต์ต้องมีทรัพยากร Dependency ใน MDC Android Support Library โค้ดตัวอย่างที่คุณดาวน์โหลดควรมีทรัพยากร Dependency นี้อยู่ในรายการแล้ว แต่การทำตามขั้นตอนต่อไปนี้ถือเป็นแนวทางปฏิบัติแนะนำเพื่อให้แน่ใจ

  1. ไปที่ไฟล์ build.gradle ของโมดูล app และตรวจสอบว่าบล็อก dependencies มีทรัพยากร Dependency ใน MDC Android ดังนี้
api 'com.google.android.material:material:1.1.0-alpha06'
  1. (ไม่บังคับ) หากจำเป็น ให้แก้ไขไฟล์ build.gradle เพื่อเพิ่มทรัพยากร Dependency ต่อไปนี้ แล้วซิงค์โปรเจ็กต์
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. ตรวจสอบว่าการกำหนดค่าบิลด์ทางด้านซ้ายของปุ่มเรียกใช้ / เล่น คือ app
  2. กดปุ่มเรียกใช้ / เล่น สีเขียวเพื่อสร้างและเรียกใช้แอป
  3. ในหน้าต่าง Select Deployment Target หากคุณมีอุปกรณ์ Android อยู่ในรายการอุปกรณ์ที่พร้อมใช้งานแล้ว ให้ข้ามไปที่ขั้นตอนที่ 8 หากไม่มี ให้คลิก Create New Virtual Device
  4. ในหน้าจอ Select Hardware ให้เลือกอุปกรณ์โทรศัพท์ เช่น Pixel 2 แล้วคลิก Next
  5. ในหน้าจอ อิมเมจระบบ ให้เลือก Android เวอร์ชันล่าสุด ซึ่งควรเป็น ระดับ API สูงสุด หากไม่ได้ติดตั้ง ให้คลิกลิงก์ดาวน์โหลด ที่แสดงขึ้น แล้วดาวน์โหลดให้เสร็จสมบูรณ์
  6. คลิกถัดไป
  7. ในหน้าจอ อุปกรณ์เสมือน Android (AVD) ให้ปล่อยการตั้งค่าไว้ตามเดิม แล้วคลิก Finish
  8. เลือกอุปกรณ์ Android จากกล่องโต้ตอบเป้าหมายการติดตั้งใช้งาน
  9. คลิกตกลง
  10. Android Studio จะสร้างแอป ติดตั้งใช้งาน และเปิดแอปในอุปกรณ์เป้าหมายโดยอัตโนมัติ

สำเร็จ! คุณควรเห็นหน้าเข้าสู่ระบบของ Shrine จาก Codelab MDC-101

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 ให้เพิ่มโค้ดต่อไปนี้ลงในคอมโพเนนต์ XML Toolbar ที่คุณเพิ่งเพิ่มลงในเลย์เอาต์

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 ปุ่มเหล่านี้เรียกว่า ปุ่มดำเนินการ เราจะจัดรูปแบบแถบแอปด้านบนและเพิ่มปุ่มดำเนินการลงในเมนูของแถบแอปโดยใช้โปรแกรม

ในฟังก์ชัน onCreateView ของ ProductGridFragment.kt ให้ตั้งค่า Toolbar ของ activity ให้ใช้เป็น ActionBar โดยใช้ setSupportActionBar คุณทำได้หลังจากสร้างมุมมองด้วย inflater แล้ว

ProductGridFragment.kt

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

   // Set up the toolbar.
   (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

   return view;
}

จากนั้น ให้เขียนโค้ดเพื่อลบล้างเมธอดที่เราเพิ่งเปลี่ยนไปเพื่อตั้งค่าแถบเครื่องมือ โดยเขียนโค้ด onCreateOptionsMenu เพื่อ Inflate เนื้อหาของ shr_toolbar_menu.xml ลงในแถบเครื่องมือ

ProductGridFragment.kt

override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
   menuInflater.inflate(R.menu.shr_toolbar_menu, menu)
   super.onCreateOptionsMenu(menu, menuInflater)
}

สุดท้าย ให้เขียนโค้ดเพื่อแทนที่ onCreate() ใน ProductGridFragment.kt และหลังจากเรียกใช้ super() แล้ว ให้เรียกใช้ setHasOptionMenu ด้วย true

ProductGridFragment.kt

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setHasOptionsMenu(true)
}

ข้อมูลโค้ดด้านบนจะตั้งค่าแถบแอปจากเลย์เอาต์ XML ให้เป็น ActionBar สำหรับกิจกรรมนี้ การเรียกกลับ onCreateOptionsMenu จะบอกกิจกรรมว่าจะใช้สิ่งใดเป็นเมนู ในกรณีนี้ ระบบจะใส่รายการเมนูจาก R.menu.shr_toolbar_menu ลงในแถบแอป ไฟล์เมนูมี 2 รายการ ได้แก่ "ค้นหา" และ "กรอง"

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.kt ควรมีลักษณะดังนี้

ProductGridFragment.kt

package com.google.codelabs.mdc.kotlin.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 androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import com.google.codelabs.mdc.kotlin.shrine.network.ProductEntry
import kotlinx.android.synthetic.main.shr_product_grid_fragment.view.*

class ProductGridFragment : Fragment() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setHasOptionsMenu(true)
   }

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

       // Set up the tool bar
       (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

       return view;
   }

   override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
       menuInflater.inflate(R.menu.shr_toolbar_menu, menu)
       super.onCreateOptionsMenu(menu, menuInflater)
   }
}

สร้างและเรียกใช้ หน้าจอหลักควรมีลักษณะดังนี้

d04e8aa3b27f4754.png

ตอนนี้แถบเครื่องมือมีไอคอนการนำทาง ชื่อ และไอคอนการดำเนินการ 2 รายการทางด้านขวา นอกจากนี้ แถบเครื่องมือยังแสดงระดับความสูงโดยใช้เงาเล็กน้อยที่แสดงว่าแถบเครื่องมืออยู่คนละเลเยอร์กับเนื้อหา

4. เพิ่มการ์ด

เมื่อแอปมีโครงสร้างแล้ว เรามาจัดระเบียบเนื้อหาโดยวางเนื้อหาไว้ในการ์ดกัน

เพิ่มการ์ด

มาเริ่มต้นด้วยการเพิ่มการ์ด 1 ใบใต้แถบแอปด้านบน การ์ดควรมีพื้นที่สำหรับรูปภาพ ชื่อ และป้ายกำกับสำหรับข้อความรอง เพิ่มโค้ดต่อไปนี้ใน 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

ในการแสดงตัวอย่างนี้ คุณจะเห็นว่าการ์ดมีการเยื้องจากขอบด้านซ้าย มีมุมโค้งมน และมีเงา (ซึ่งแสดงระดับความสูงของการ์ด) องค์ประกอบทั้งหมดเรียกว่า "คอนเทนเนอร์" นอกเหนือจากคอนเทนเนอร์แล้ว องค์ประกอบทั้งหมดภายในคอนเทนเนอร์เป็นค่าที่ไม่บังคับ

คุณสามารถเพิ่มองค์ประกอบต่อไปนี้ลงในคอนเทนเนอร์ ได้แก่ ข้อความส่วนหัว ภาพขนาดย่อหรือรูปโปรไฟล์ ข้อความหัวข้อย่อย ตัวคั่น รวมถึงปุ่มและไอคอน ตัวอย่างเช่น การ์ดที่เราเพิ่งสร้างมี TextView 2 รายการ (รายการหนึ่งสำหรับชื่อและอีกรายการหนึ่งสำหรับข้อความรอง) ใน LinearLayout ซึ่งจัดแนวไว้ที่ด้านล่างของการ์ด

โดยปกติการ์ดจะแสดงในคอลเล็กชันที่มีการ์ดอื่นๆ ในส่วนถัดไปของ 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 ซึ่งช่วยให้เราโหลดและแสดงรูปภาพจาก URL ได้) และ TextViews 2 รายการ

จากนั้นดู ProductCardRecyclerViewAdapter ที่เราเตรียมไว้ให้ ซึ่งอยู่ในแพ็กเกจเดียวกับ ProductGridFragment

ProductCardRecyclerViewAdapter.kt

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

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

import com.google.codelabs.mdc.kotlin.shrine.network.ProductEntry

/**
* Adapter used to show a simple grid of products.
*/
class ProductCardRecyclerViewAdapter(private val productList: List<ProductEntry>) : RecyclerView.Adapter<ProductCardViewHolder>() {

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductCardViewHolder {
       val layoutView = LayoutInflater.from(parent.context).inflate(R.layout.shr_product_card, parent, false)
       return ProductCardViewHolder(layoutView)
   }

   override fun onBindViewHolder(holder: ProductCardViewHolder, position: Int) {
       // TODO: Put ViewHolder binding code here in MDC-102
   }

   override fun getItemCount(): Int {
       return productList.size
   }
}

คลาส Adapter ด้านบนจะจัดการเนื้อหาของตาราง เราจะเขียนโค้ดสำหรับ onBindViewHolder() ในเร็วๆ นี้เพื่อกำหนดสิ่งที่มุมมองแต่ละรายการควรทำกับเนื้อหาที่กำหนด

นอกจากนี้ คุณยังดู ProductCardViewHolder ในแพ็กเกจเดียวกันได้ด้วย คลาสนี้จะจัดเก็บมุมมองที่ส่งผลต่อเลย์เอาต์การ์ด เพื่อให้เราแก้ไขมุมมองเหล่านั้นได้ในภายหลัง

ProductCardViewHolder.kt

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

import android.view.View
import androidx.recyclerview.widget.RecyclerView

class ProductCardViewHolder(itemView: View) //TODO: Find and store views from itemView
   : RecyclerView.ViewHolder(itemView)

หากต้องการตั้งค่าตาราง ขั้นแรกเราจะต้องนำ MaterialCardView ตัวยึดตำแหน่งออกจาก shr_product_grid_fragment.xml จากนั้นคุณควรเพิ่มคอมโพเนนต์ที่แสดงตารางการ์ด ในกรณีนี้ เราจะใช้ RecyclerView เพิ่มคอมโพเนนต์ RecyclerView ลงใน shr_product_grid_fragment.xml ใต้คอมโพเนนต์ 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 ควรมีลักษณะดังนี้

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() ให้เพิ่มโค้ดการเริ่มต้น RecyclerView ลงใน ProductGridFragment.kt หลังจากเรียกใช้ setUpToolbar(view) และก่อนคำสั่ง return

ProductGridFragment.kt

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

   // Set up the toolbar.
   (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

   // Set up the RecyclerView
   view.recycler_view.setHasFixedSize(true)
   view.recycler_view.layoutManager = GridLayoutManager(context, 2, RecyclerView.VERTICAL, false)
   val adapter = ProductCardRecyclerViewAdapter(
           ProductEntry.initProductEntryList(resources))
   view.recycler_view.adapter = adapter
   val largePadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing)
   val smallPadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small)
   view.recycler_view.addItemDecoration(ProductGridItemDecoration(largePadding, smallPadding))

   return view;
}

ข้อมูลโค้ดด้านบนมีขั้นตอนการเริ่มต้นที่จำเป็นในการตั้งค่า RecyclerView ซึ่งรวมถึงการตั้งค่า Layout Manager ของ RecyclerView รวมถึงการเริ่มต้นและตั้งค่า Adapter ของ RecyclerView

ตอนนี้ไฟล์ ProductGridFragment.kt ควรมีลักษณะดังนี้

ProductGridFragment .kt

package com.google.codelabs.mdc.kotlin.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 androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.codelabs.mdc.kotlin.shrine.network.ProductEntry
import kotlinx.android.synthetic.main.shr_product_grid_fragment.view.*

class ProductGridFragment : Fragment() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setHasOptionsMenu(true)
   }

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

       // Set up the toolbar.
       (activity as AppCompatActivity).setSupportActionBar(view.app_bar)

       // Set up the RecyclerView
       view.recycler_view.setHasFixedSize(true)
       view.recycler_view.layoutManager = GridLayoutManager(context, 2, RecyclerView.VERTICAL, false)
       val adapter = ProductCardRecyclerViewAdapter(
               ProductEntry.initProductEntryList(resources))
       view.recycler_view.adapter = adapter
       val largePadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing)
       val smallPadding = resources.getDimensionPixelSize(R.dimen.shr_product_grid_spacing_small)
       view.recycler_view.addItemDecoration(ProductGridItemDecoration(largePadding, smallPadding))

       return view;
   }

   override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
       menuInflater.inflate(R.menu.shr_toolbar_menu, menu)
       super.onCreateOptionsMenu(menu, menuInflater)
   }
}

สร้างและเรียกใช้

f9aeab846fc3bb4c.png

ตอนนี้การ์ดปรากฏขึ้นแล้ว แต่ยังไม่แสดงอะไรเลย เรามาเพิ่มข้อมูลสินค้ากัน

เพิ่มรูปภาพและข้อความ

เพิ่มรูปภาพ ชื่อสินค้า และราคาสำหรับการ์ดแต่ละใบ การแยกส่วน ViewHolder จะเก็บมุมมองสำหรับการ์ดแต่ละใบ เพิ่มมุมมอง 3 รายการใน ViewHolder ดังนี้

ProductCardViewHolder.kt

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

import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

import com.android.volley.toolbox.NetworkImageView

class ProductCardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

   var productImage: NetworkImageView = itemView.findViewById(R.id.product_image)
   var productTitle: TextView = itemView.findViewById(R.id.product_title)
   var productPrice: TextView = itemView.findViewById(R.id.product_price)
}

อัปเดตเมธอด onBindViewHolder() ใน ProductCardRecyclerViewAdapter เพื่อตั้งค่าชื่อ ราคา และรูปภาพสินค้าสำหรับมุมมองผลิตภัณฑ์แต่ละรายการ ดังที่แสดงด้านล่าง

ProductCardRecyclerViewAdapter.kt

override fun onBindViewHolder(holder: ProductCardViewHolder, position: Int) {
   if (position < productList.size) {
       val product = productList[position]
       holder.productTitle.text = product.title
       holder.productPrice.text = product.price
       ImageRequester.setImageFromUrl(holder.productImage, product.url)
   }
}

โค้ดด้านบนจะบอก Adapter ของ RecyclerView ว่าควรทำอย่างไรกับการ์ดแต่ละใบ โดยใช้ ViewHolder

โดยโค้ดจะตั้งค่าข้อมูลข้อความใน TextView แต่ละรายการของ ViewHolder และเรียกใช้ ImageRequester เพื่อรับรูปภาพจาก URL ImageRequester เป็นคลาสที่เราเตรียมไว้เพื่อความสะดวกของคุณ และใช้ไลบรารี Volley (ซึ่งเป็นหัวข้อที่อยู่นอกขอบเขตของ Codelab นี้ แต่คุณสามารถสำรวจโค้ดได้ด้วยตนเอง)

สร้างและเรียกใช้

249db074eff043f4.png

ตอนนี้ผลิตภัณฑ์ของเราแสดงในแอปแล้ว

6. สรุป

แอปของเรามีโฟลว์พื้นฐานที่นำผู้ใช้จากหน้าจอเข้าสู่ระบบไปยังหน้าจอหลัก ซึ่งผู้ใช้สามารถดูผลิตภัณฑ์ได้ เราได้เพิ่มแถบแอปด้านบนที่มีชื่อและปุ่ม 3 ปุ่ม รวมถึงตารางการ์ดเพื่อแสดงเนื้อหาของแอป โดยใช้โค้ดเพียงไม่กี่บรรทัด ตอนนี้หน้าจอหลักของเราเรียบง่ายและใช้งานได้จริง โดยมีโครงสร้างพื้นฐานและเนื้อหาที่ดำเนินการได้

ขั้นตอนถัดไป

ตอนนี้เราได้ใช้คอมโพเนนต์หลัก 4 รายการของ Material Design จากไลบรารี MDC-Android แล้ว ได้แก่ แถบแอปด้านบน การ์ด ช่องข้อความ และปุ่ม คุณสามารถสำรวจคอมโพเนนต์อื่นๆ ได้โดยไปที่แคตตาล็อก MDC-Android

แม้ว่าแอปจะทำงานได้อย่างสมบูรณ์ แต่แอปยังไม่ได้แสดงแบรนด์หรือสไตล์ใดๆ ที่เฉพาะเจาะจง ใน MDC-103: การกำหนดธีม Material Design ด้วยสี รูปร่าง ระดับความสูง และประเภท เราจะปรับแต่งสไตล์ของคอมโพเนนต์เหล่านี้เพื่อแสดงแบรนด์ที่สดใสและทันสมัย

ฉันทำ Codelab นี้เสร็จได้โดยใช้เวลาและความพยายามที่สมเหตุสมผล

เห็นด้วยอย่างยิ่ง เห็นด้วย เป็นกลาง ไม่เห็นด้วย ไม่เห็นด้วยอย่างยิ่ง

ฉันต้องการใช้ Material Components ต่อไปในอนาคต

เห็นด้วยอย่างยิ่ง เห็นด้วย เป็นกลาง ไม่เห็นด้วย ไม่เห็นด้วยอย่างยิ่ง