MDC-102 Android: מבנה ופריסה של חומרים (Kotlin)

1. מבוא

logo_components_color_2x_web_96dp.png

‏Material Components‏ (MDC) עוזרים למפתחים להטמיע את Material Design. MDC נוצר על ידי צוות של מהנדסים ומעצבי חוויית משתמש ב-Google, והוא כולל עשרות רכיבי ממשק משתמש יפים ופונקציונליים. הוא זמין ל-Android, ל-iOS, לאינטרנט ול-Flutter.material.io/develop

ב-codelab MDC-101 השתמשתם בשני רכיבי Material Components ‏(MDC) כדי ליצור דף כניסה: שדות טקסט ולחצנים עם תנודות דיו. עכשיו נרחיב את הבסיס הזה על ידי הוספת ניווט, מבנה ונתונים.

מה תפַתחו

בסדנת הקוד הזו תלמדו ליצור מסך בית לאפליקציה בשם Shrine, אפליקציית מסחר אלקטרוני שמוכרת בגדים ומוצרים לבית. היא תכלול את הפרטים הבאים:

  • סרגל אפליקציה בחלק העליון
  • רשימת תצוגה של מוצרים

249db074eff043f4.png

רכיבי MDC-Android ב-Codelab הזה

  • AppBarLayout
  • MaterialCardView

מה צריך להכין

  • ידע בסיסי בפיתוח Android
  • Android Studio (אם עדיין לא עשיתם זאת, אתם יכולים להוריד את התוכנה כאן)
  • אמולטור או מכשיר של Android (האפשרות זמינה ב-Android Studio)
  • קוד לדוגמה (ראו שלב הבא)

מה מידת הניסיון שלך בפיתוח אפליקציות ל-Android?

מתחילים בינוניים מתקדמים

2. הגדרת סביבת הפיתוח

ממשיכים לעבור מ-MDC-101?

אם השלמתם את קורס MDC-101, הקוד שלכם אמור להיות מוכן לקודלאב הזה. אפשר לדלג לשלב 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 -> Temple (או מחפשים במחשב את shrine כדי לפתוח את פרויקט המשלוחים).
  2. ממתינים רגע עד שמערכת Android Studio תסיים לבנות ולסנכרן את הפרויקט, כפי שמוצג באינדיקטורים של הפעילות בחלק התחתון של החלון של Android Studio.
  3. בשלב הזה, יכול להיות ש-Android Studio תציג כמה שגיאות build כי חסרים לכם Android SDK או כלי build, כמו השגיאה שמוצגת בהמשך. פועלים לפי ההוראות ב-Android Studio כדי להתקין או לעדכן את הרכיבים האלה ולסנכרן את הפרויקט.

KzoYWC1S7Se7yL8igi1vXF_mbVxAdl2lg5kb7RODrsVpEng0G6U3NK1Qnn0faBBZd2u71yMXioy9tD-7fv3NXvVO4N3EtMMeWDTmqBMMl6egd9R5uXX0T_SKmahbmRor3wZZHX0ByA

הוספת יחסי תלות לפרויקט

צריך להוסיף לפרויקט תלות בספריית התמיכה של 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. מוודאים שתצורת ה-build שמשמאל ללחצן ההפעלה / ההפעלה היא app.
  2. לוחצים על הלחצן הירוק Run (הפעלה) כדי ליצור ולהריץ את האפליקציה.
  3. בחלון בחירת יעד פריסה, אם כבר יש לכם מכשיר Android שרשום ברשימת המכשירים הזמינים, מדלגים לשלב 8. אם לא, לוחצים על Create New Virtual Device (יצירת מכשיר וירטואלי חדש).
  4. במסך Select Hardware (בחירת חומרה), בוחרים מכשיר טלפון, כמו Pixel 2, ולוחצים על Next (הבא).
  5. במסך System Image, בוחרים גרסת Android עדכנית, רצוי ברמת ה-API הגבוהה ביותר. אם היא לא מותקנת, לוחצים על הקישור הורדה שמופיע ומשלימים את ההורדה.
  6. לוחצים על הבא.
  7. במסך Android Virtual Device (AVD), משאירים את ההגדרות כפי שהן ולוחצים על Finish.
  8. בוחרים מכשיר Android מתיבת הדו-שיח של יעד הפריסה.
  9. לוחצים על אישור.
  10. Android Studio יוצר את האפליקציה, פורס אותה ופותח אותה באופן אוטומטי במכשיר היעד.

הצלחת! אמורה להופיע דף הכניסה של Shrine מהקודלאב של 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 כדי לנפח את התוכן של 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 שלנו כסרגל הפעולות של הפעילות הזו. הקריאה החוזרת 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.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

עכשיו סרגל הכלים כולל סמל ניווט, כותרת ושני סמלי פעולות בצד שמאל. בסרגל הכלים מוצגת גם תצוגה של הגובה באמצעות צללית עדינה שמראה שהוא נמצא בשכבה שונה מהתוכן.

4. הוספת כרטיס

עכשיו, אחרי שיש לאפליקציה שלנו מבנה כלשהו, נארגן את התוכן על ידי הצבתו בכרטיסים.

הוספת כרטיס

כדי להתחיל, מוסיפים כרטיס אחד מתחת לסרגל האפליקציות העליון. כרטיס צריך לכלול אזור לתמונה, כותרת ותווית לטקסט משני. מוסיפים את הטקסט הבא בקובץ shr_product_grid_fragment.xml מתחת ל-AppBarLayout.

shr_product_grid_fragment.xml

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

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

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

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

פיתוח והרצה:

f6184a55ccb5f920.png

בתצוגה המקדימה הזו ניתן לראות שהכרטיס ממוקם מהקצה השמאלי, ויש לו פינות מעוגלות וצל (שמבטא את גובה הכרטיס). הרכיב כולו נקרא 'מאגר'. מלבד הקונטיינר, כל הרכיבים שבו הם אופציונליים.

אפשר להוסיף את הרכיבים הבאים למאגר תגים: טקסט כותרת, תמונה ממוזערת או דמות, טקסט כותרת משנה, מחיצות ואפילו לחצנים וסמלים. למשל, הכרטיס שיצרנו מכיל שתי שורות TextView (אחת לכותרת ואחת לטקסט משני) בתוך LinearLayout, שממולאות בחלק התחתון של הכרטיס.

בדרך כלל הכרטיסים מוצגים באוסף עם כרטיסים אחרים. בקטע הבא של סדנת הקוד הזו, נציג אותם כקולקציה בתוך רשת.

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

כדי להגדיר את הרשת שלנו, קודם אנחנו רוצים להסיר את ה-placeholder 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. ההגדרה כוללת את הגדרת מנהל הפריסה של 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)
   }
}

פיתוח והרצה:

f9aeab846fc3bb4c.png

הכרטיסים כבר שם! עדיין לא מוצגים בהם מוצרים, לכן נוסיף נתוני מוצרים.

הוספת תמונות וטקסט

לכל כרטיס, מוסיפים תמונה, שם מוצר ומחיר. בהפשטה שלנו ViewHolder מפורטות הצפיות בכל כרטיס. ב-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)
   }
}

הקוד שלמעלה אומר למתאם של RecyclerView מה לעשות עם כל כרטיס, באמצעות ViewHolder.

כאן הוא מגדיר את נתוני הטקסט בכל אחד מה-TextView של ViewHolder, וקורא ל-ImageRequester כדי לקבל תמונה מכתובת URL. ImageRequester היא כיתה שסיפקנו לנוחיותכם, והיא משתמשת בספרייה Volley (זהו נושא מחוץ להיקף של Codelab הזה, אבל אתם יכולים לבדוק את הקוד בעצמכם).

יצירה והפעלה:

249db074eff043f4.png

המוצרים שלנו מוצגים עכשיו באפליקציה!

6. Recap

באפליקציה שלנו יש תהליך בסיסי שמנחה את המשתמש ממסך ההתחברות למסך הבית, שבו אפשר להציג מוצרים. בעזרת כמה שורות קוד, הוספנו סרגל אפליקציה בחלק העליון עם כותרת ושלושה לחצנים, ורשת של כרטיסים להצגת התוכן של האפליקציה. מסך הבית שלנו עכשיו פשוט ופונקציונלי, עם מבנה בסיסי ותוכן פרקטי.

השלבים הבאים

בעזרת שורת האפליקציות העליונה, הכרטיס, שדה הטקסט והלחצן, השתמשנו עכשיו בארבעה רכיבים מרכזיים של Material Design מספריית MDC-Android! ב-MDC-Android Catalog תוכלו למצוא עוד רכיבים.

למרות שהיא פועלת באופן מלא, האפליקציה שלנו לא מבטאת עדיין מותג או סגנון מסוים. במשחק MDC-103: עיצוב חומרים בעיצוב חדשני עם צבע, צורה, גובה וסוג, נתאים אישית את הסגנון של הרכיבים האלה כדי לבטא מותג מודרני ותוסס.

הצלחתי להשלים את הקודלהב הזה בזמן ובמאמץ סבירים

נכון מאוד נכון ניטרלי לא נכון לא נכון בכלל

אני רוצה להמשיך להשתמש ב-Material Components בעתיד

נכון מאוד נכון ניטרלי לא נכון לא נכון בכלל