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

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 (MDC) 2 รายการเพื่อสร้างหน้าเข้าสู่ระบบ ได้แก่ ช่องข้อความและปุ่มที่มีระลอกหมึก ตอนนี้มาต่อยอดจากพื้นฐานนี้ด้วยการเพิ่มการนำทาง โครงสร้าง และข้อมูลกัน

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

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

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

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

  • AppBarLayout
  • MaterialCardView

สิ่งที่ต้องมี

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

คุณจะให้คะแนนระดับประสบการณ์ในการสร้างแอป Android เท่าใด

ผู้เริ่มต้น ระดับกลาง ผู้ชำนาญ

หากเคยเรียน MDC-101 มาแล้ว

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

หากเพิ่งเริ่มต้น

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

ดาวน์โหลดแอปเริ่มต้น

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

เพิ่มการพึ่งพาโปรเจ็กต์

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

  1. ไปที่ไฟล์ build.gradle ของโมดูล app และตรวจสอบว่าบล็อก 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. ตรวจสอบว่าการกำหนดค่าบิลด์ทางด้านซ้ายของปุ่มเรียกใช้ / เล่นเป็น app
  2. กดปุ่มเรียกใช้ / เล่นสีเขียวเพื่อสร้างและเรียกใช้แอป
  3. ในหน้าต่างเลือกเป้าหมายการติดตั้งใช้งาน หากมีอุปกรณ์ Android แสดงอยู่ในอุปกรณ์ที่พร้อมใช้งานแล้ว ให้ข้ามไปยังขั้นตอนที่ 8 หรือคลิกสร้างอุปกรณ์เสมือนใหม่
  4. ในหน้าจอเลือกฮาร์ดแวร์ ให้เลือกอุปกรณ์โทรศัพท์ เช่น Pixel 2 แล้วคลิกถัดไป
  5. ในหน้าจออิมเมจระบบ ให้เลือกเวอร์ชัน Android ล่าสุด โดยควรเลือกระดับ API สูงสุด หากไม่ได้ติดตั้ง ให้คลิกลิงก์ดาวน์โหลดที่แสดงและดาวน์โหลดให้เสร็จสมบูรณ์
  6. คลิกถัดไป
  7. ในหน้าจออุปกรณ์เสมือน Android (AVD) ให้ปล่อยการตั้งค่าไว้ตามเดิม แล้วคลิกเสร็จสิ้น
  8. เลือกอุปกรณ์ Android จากกล่องโต้ตอบเป้าหมายการติดตั้งใช้งาน
  9. คลิกตกลง
  10. Android Studio จะสร้างแอป นำไปใช้งาน และเปิดแอปในอุปกรณ์เป้าหมายโดยอัตโนมัติ

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

ตอนนี้หน้าจอเข้าสู่ระบบดูดีแล้ว มาป้อนข้อมูลผลิตภัณฑ์บางอย่างลงในแอปกัน

หน้าจอหลักจะปรากฏขึ้นเมื่อปิดหน้าเข้าสู่ระบบ โดยจะมีหน้าจอที่ระบุว่า "คุณทำสำเร็จแล้ว!" เยี่ยมเลย แต่ตอนนี้ผู้ใช้ของเราไม่มีการดำเนินการใดๆ หรือไม่รู้ว่าตนเองอยู่ที่ใดในแอป เราจึงต้องเพิ่มการนำทางเพื่อช่วยในเรื่องนี้

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

ในฟังก์ชัน 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 เพื่อขยายเนื้อหาของ 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 ให้เป็นแถบการดำเนินการสำหรับกิจกรรมนี้ Callback 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)
   }
}

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

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

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

เพิ่มบัตร

มาเริ่มกันด้วยการเพิ่มการ์ด 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>

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

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

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

โดยปกติแล้ว การ์ดจะแสดงในคอลเล็กชันพร้อมกับการ์ดอื่นๆ ในส่วนถัดไปของ 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
   }
}

คลาสอะแดปเตอร์ด้านบนจะจัดการเนื้อหาของตารางกริด เราจะเขียนโค้ดสำหรับ 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 ใต้คอมโพเนนต์ 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>

สุดท้าย ใน 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 ซึ่งรวมถึงการตั้งค่าเครื่องมือจัดการเลย์เอาต์ของ RecyclerView รวมถึงการเริ่มต้นและตั้งค่าอแดปเตอร์ของ 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)
   }
}

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

ตอนนี้การ์ดพร้อมให้ใช้งานแล้ว ตอนนี้ยังไม่มีอะไรแสดง เรามาเพิ่มข้อมูลผลิตภัณฑ์กัน

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

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

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

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

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

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

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

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

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

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

แม้ว่าแอปจะทำงานได้อย่างเต็มที่ แต่ก็ยังไม่ได้แสดงถึงแบรนด์หรือสไตล์ใดๆ ใน MDC-103: Material Design Theming with Color, Shape, Elevation and Type เราจะปรับแต่งสไตล์ของคอมโพเนนต์เหล่านี้เพื่อแสดงถึงแบรนด์ที่สดใสและทันสมัย

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

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

ฉันต้องการใช้คอมโพเนนต์ Material ต่อไปในอนาคต

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