MDC-101 Android: יסודות החומר (MDC) - יסודות (Java)

1. מבוא

logo_components_color_2x_web_96dp.png

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

מהם Material Design ו-Material Components ל-Android?

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

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

ב-Codelab הזה, תבנו דף כניסה באמצעות כמה רכיבים של MDC Android.

מה תפַתחו

ה-Codelab הזה הוא הראשון מתוך 4 שיעורים שבהם נלמד איך ליצור אפליקציה בשם Shrine, אפליקציית מסחר אלקטרוני ל-Android שמוכרת בגדים ומוצרים לבית. במאמר נסביר איך אפשר להתאים אישית רכיבים כדי לשקף מותג או סגנון כלשהו באמצעות MDC-Android.

ב-codelab הזה תיצרו דף כניסה לאפליקציית Shrine, שיכלול:

  • שני שדות טקסט, אחד להזנת שם משתמש והשני להזנת סיסמה
  • שני לחצנים, אחד ל'ביטול' ואחד ל'הבא'
  • שם האפליקציה (Shrine)
  • תמונה של הלוגו של Shrine

4cb0c218948144b4.png

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

  • שדה טקסט
  • כפתור

מה תצטרכו

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

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

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

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

הפעלת Android Studio

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

הורדת אפליקציית ה-codelab למתחילים

אפליקציה לתחילת הדרך נמצאת בספרייה material-components-android-codelabs-101-starter/java.

...או לשכפל אותו מ-GitHub

כדי לשכפל את ה-codelab הזה מ-GitHub, מריצים את הפקודות הבאות:

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

טעינת קוד לתחילת הדרך ב-Android Studio

  1. אחרי שאשף ההגדרה יסיים את הפעולה ויוצג החלון Welcome to Android Studio (ברוכים הבאים ל-Android Studio), לוחצים על Open an existing Android Studio project (פתיחת פרויקט קיים של Android Studio). עוברים אל הספרייה שבה התקנתם את קוד הדוגמה ובוחרים באפשרות java -> shrine (או מחפשים במחשב את shrine) כדי לפתוח את פרויקט Shrine.
  2. מחכים כמה רגעים עד ש-Android Studio יבנה ויסנכרן את הפרויקט, כפי שמצוין על ידי אינדיקטורים של פעילות לאורך החלק התחתון של חלון Android Studio.
  3. בשלב הזה, יכול להיות ש-Android Studio יציג שגיאות build כי חסר לכם Android SDK או כלי build, כמו השגיאה שמוצגת למטה. פועלים לפי ההוראות ב-Android Studio כדי להתקין או לעדכן את התוספים האלה ולסנכרן את הפרויקט.

F5H6srsw_5xOPGFpKrm1RwgewatxA_HUbDI1PWoQUAoJcT6DpfBOkAYwq3S-2vUHvweUaFgAmG7BtUKkGouUbhTwXQh53qec8tO5eVecdlo7QIoLc8rNxFEBb8l7RlS-KzBbZOzVhA

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

הפרויקט צריך להיות תלוי בספריית התמיכה של 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 / Play כדי ליצור ולהריץ את האפליקציה.
  3. בחלון Select Deployment Target (בחירת יעד הפריסה), אם כבר מופיע מכשיר Android ברשימת המכשירים הזמינים, מדלגים אל שלב 8. אם לא, לוחצים על יצירת מכשיר וירטואלי חדש.
  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 צריך לפעול באמולטור. השם Shrine והלוגו של Shrine אמורים להופיע מתחתיו.

e7ed014e84755811.png

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

פותחים את MainActivity.java בספרייה shrine -> app -> src -> main -> java -> com.google.codelabs.mdc.java.shrine. היא צריכה לכלול את הפרטים הבאים:

MainActivity.java

package com.google.codelabs.mdc.java.shrine;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;

public class MainActivity extends AppCompatActivity implements NavigationHost {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.shr_main_activity);

       if (savedInstanceState == null) {
           getSupportFragmentManager()
                   .beginTransaction()
                   .add(R.id.container, new LoginFragment())
                   .commit();
       }
   }

   /**
    * Navigate to the given fragment.
    *
    * @param fragment       Fragment to navigate to.
    * @param addToBackstack Whether or not the current fragment should be added to the backstack.
    */
   @Override
   public void navigateTo(Fragment fragment, boolean addToBackstack) {
       FragmentTransaction transaction =
               getSupportFragmentManager()
                       .beginTransaction()
                       .replace(R.id.container, fragment);

       if (addToBackstack) {
           transaction.addToBackStack(null);
       }

       transaction.commit();
   }
}

הפעילות הזו מציגה את קובץ הפריסה R.layout.shr_main_activity, שמוגדר ב-shr_main_activity.xml.

אפשר לראות שבonCreate(), MainActivity.java מתחילה עסקה של Fragment כדי להציג את LoginFragment. LoginFragment. זה מה שנשנה בסדנת הקוד הזו. בפעילות מיושמת גם שיטת navigateTo(Fragment), שמוגדרת ב-NavigationHost, שמאפשרת לכל מקטע לעבור למקטע אחר.

מקישים על Command + Click (או על Control + Click) shr_main_activity בקובץ הפעילות כדי לפתוח את קובץ הפריסה, או עוברים לקובץ הפריסה ב-app -> res -> layout -> shr_main_activity.xml.

shr_main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/container"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity" />

כאן אנחנו רואים <FrameLayout> פשוט שמשמש כמאגר לכל המקטעים שהפעילות מציגה. בואו נפתח את LoginFragment.java.

LoginFragment.java

package com.google.codelabs.mdc.java.shrine;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

/**
* Fragment representing the login screen for Shrine.
*/
public class LoginFragment extends Fragment {

   @Override
   public View onCreateView(
           @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       // Inflate the layout for this fragment
       View view = inflater.inflate(R.layout.shr_login_fragment, container, false);

       // Snippet from "Navigate to the next Fragment" section goes here.

       return view;
   }

   // "isPasswordValid" from "Navigate to the next Fragment" section method goes here
}

LoginFragment מבצע inflate לקובץ הפריסה shr_login_fragment ומציג אותו ב-onCreateView(). בואו נסתכל על קובץ הפריסה shr_login_fragment.xml כדי לראות איך נראה דף הכניסה.

shr_login_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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"
   android:background="@color/loginPageBackgroundColor"
   tools:context=".LoginFragment">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:clipChildren="false"
       android:clipToPadding="false"
       android:orientation="vertical"
       android:padding="24dp"
       android:paddingTop="16dp">

       <ImageView
           android:layout_width="64dp"
           android:layout_height="64dp"
           android:layout_gravity="center_horizontal"
           android:layout_marginTop="48dp"
           android:layout_marginBottom="16dp"
           app:srcCompat="@drawable/shr_logo" />

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center_horizontal"
           android:layout_marginBottom="132dp"
           android:text="@string/shr_app_name"
           android:textAllCaps="true"
           android:textSize="16sp" />

       <!-- Snippet from "Add text fields" section goes here. -->

       <!-- Snippet from "Add buttons" section goes here. -->

   </LinearLayout>
</ScrollView>

כאן אפשר לראות את הלוגו <LinearLayout> עם <ImageView> בחלק העליון, שמייצג את הלוגו של Shrine.

אחריו מופיע התג <TextView> שמייצג את התווית 'SHRINE'. הטקסט של התווית הזו הוא משאב מחרוזת בשם @string/shr_app_name. אם לוחצים על Command + Click (או על Control + Click) על שם משאב המחרוזת, או פותחים את app -> res -> values -> strings.xml, אפשר לראות את הקובץ strings.xml שבו מוגדרים משאבי המחרוזת. כשנוסיף בעתיד עוד משאבי מחרוזות, הם יוגדרו כאן. לכל משאב בקובץ הזה צריך להיות הקידומת shr_ כדי לציין שהוא חלק מאפליקציית Shrine.

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

3. הוספת שדות טקסט

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

d83c47fb4aed3a82.png

הוספת ה-XML

ב-shr_login_fragment.xml, מוסיפים שני רכיבי TextInputLayout עם רכיב צאצא TextInputEditText בתוך <LinearLayout>, מתחת לתווית SHRINE <TextView>:

shr_login_fragment.xml

<com.google.android.material.textfield.TextInputLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_username">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="text"
       android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/password_text_input"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_password">

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/password_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>

קטע הקוד שלמעלה מייצג שני שדות טקסט, שכל אחד מהם מורכב מרכיב <TextInputLayout> ומרכיב צאצא <TextInputEditText>. טקסט העזרה של כל שדה טקסט מצוין במאפיין android:hint.

הוספנו שני משאבי מחרוזות חדשים לשדה הטקסט – @string/shr_hint_username ו-@string/shr_hint_password. אפשר לפתוח את strings.xml כדי לראות את משאבי המחרוזות האלה.

strings.xml

...
<string name="shr_hint_username">Username</string>
<string name="shr_hint_password">Password</string>
...

הוספת אימות קלט

רכיבי TextInputLayout מספקים פונקציונליות מובנית של משוב על שגיאות.

כדי להציג משוב על שגיאות, מבצעים את השינויים הבאים ב-shr_login_fragment.xml:

  • מגדירים את המאפיין app:errorEnabled לערך true ברכיב Password TextInputLayout. כך תתווסף ריווח פנימי נוסף להודעת השגיאה שמתחת לשדה הטקסט.
  • במאפיין android:inputType של רכיב הסיסמה TextInputEditText, מגדירים את הערך textPassword. הטקסט שמוזן בשדה הסיסמה יוסתר.

בעקבות השינויים האלה, שדות הטקסט ב-shr_login_fragment.xml אמורים להיראות כך:

shr_login_fragment.xml

<com.google.android.material.textfield.TextInputLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_username">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="text"
       android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/password_text_input"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_password"
   app:errorEnabled="true">

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/password_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>

עכשיו מנסים להריץ את האפליקציה. אמור להופיע דף עם שני שדות טקסט: Username (שם משתמש) ו-Password (סיסמה).

כדאי לבדוק את האנימציה של התווית הצפה:

333184b615aed4f7.gif

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

בשלב הבא, נוסיף שני לחצנים לדף הכניסה: 'ביטול' ו'הבא'. נשתמש ברכיב MDC Button, שכולל את אפקט האדווה האייקוני של Material Design.

4cb0c218948144b4.png

הוספת ה-XML

ב-shr_login_fragment.xml, מוסיפים <RelativeLayout> ל-<LinearLayout>, מתחת לרכיבי TextInputLayout. לאחר מכן מוסיפים שני רכיבי <MaterialButton> לרכיב <RelativeLayout>.

קובץ ה-XML שמתקבל צריך להיראות כך:

shr_login_fragment.xml

<RelativeLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <com.google.android.material.button.MaterialButton
       android:id="@+id/next_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentEnd="true"
       android:layout_alignParentRight="true"
       android:text="@string/shr_button_next" />

   <com.google.android.material.button.MaterialButton
       android:id="@+id/cancel_button"
       style="@style/Widget.MaterialComponents.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginEnd="12dp"
       android:layout_marginRight="12dp"
       android:layout_toStartOf="@id/next_button"
       android:layout_toLeftOf="@id/next_button"
       android:text="@string/shr_button_cancel" />

</RelativeLayout>

זהו! כשמפעילים את האפליקציה, מופיע אפקט של דיו כשמקישים על כל כפתור.

9dd162d65e4a92a2.gif

5. ניווט אל Fragment הבא

לבסוף, נוסיף קוד Java ל-LoginFragment.java כדי לקשר את הלחצן NEXT (הבא) שלנו למקטע (fragment) אחר. אפשר לראות שלכל אחד מהרכיבים שהוספנו לפריסה הוקצה id. נשתמש בids האלה כדי להפנות לרכיבים בקוד שלנו ולהוסיף בדיקות שגיאות וניווט.

נוסיף שיטה בוליאנית פרטית isPasswordValid ב-LoginFragment.java מתחת ל-onCreateView(), עם לוגיקה לקביעה אם הסיסמה תקפה או לא. לצורך ההדגמה הזו, נדאג שהסיסמה תהיה באורך של 8 תווים לפחות:

LoginFragment.java

/*
   In reality, this will have more complex logic including, but not limited to, actual
   authentication of the username and password.
*/
private boolean isPasswordValid(@Nullable Editable text) {
   return text != null && text.length() >= 8;
}

לאחר מכן, מוסיפים מאזין ללחיצות ללחצן 'הבא' שמגדיר ומנקה את השגיאה על סמך השיטה isPasswordValid() שיצרנו. ב-onCreateView(), צריך למקם את מאזין הקליקים הזה בין שורת ה-inflater לבין השורה return view.

עכשיו נוסיף פונקציית event listener לסיסמה TextInputEditText כדי להאזין לאירועים של מקשים שיגרמו לניקוי השגיאה. המאזין הזה צריך גם להשתמש ב-isPasswordValid() כדי לבדוק אם הסיסמה תקפה או לא. אפשר להוסיף את הקוד הזה ישירות מתחת ל-click listener ב-onCreateView().

השיטה onCreateView() אמורה להיראות עכשיו כך:

LoginFragment.java

@Override
public View onCreateView(
       @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   // Inflate the layout for this fragment
   View view = inflater.inflate(R.layout.shr_login_fragment, container, false);
   final TextInputLayout passwordTextInput = view.findViewById(R.id.password_text_input);
   final TextInputEditText passwordEditText = view.findViewById(R.id.password_edit_text);
   MaterialButton nextButton = view.findViewById(R.id.next_button);

   // Set an error if the password is less than 8 characters.
   nextButton.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           if (!isPasswordValid(passwordEditText.getText())) {
               passwordTextInput.setError(getString(R.string.shr_error_password));
           } else {
               passwordTextInput.setError(null); // Clear the error
           }
       }
   });

   // Clear the error once more than 8 characters are typed.
   passwordEditText.setOnKeyListener(new View.OnKeyListener() {
       @Override
       public boolean onKey(View view, int i, KeyEvent keyEvent) {
           if (isPasswordValid(passwordEditText.getText())) {
               passwordTextInput.setError(null); //Clear the error
           }
           return false;
       }
   });
   return view;
}            

עכשיו אפשר לעבור אל מקטע (fragment) אחר. מעדכנים את OnClickListener ב-onCreateView() כדי לעבור לקטע אחר כשהאימות של השגיאה מצליח. כדי לעשות את זה, מוסיפים את השורה הבאה כדי לנווט מ-ProductGridFragment אל מקרה else של מאזין הקליקים:

LoginFragment.java

...
((NavigationHost) getActivity()).navigateTo(new ProductGridFragment(), false); // Navigate to the next Fragment
...

עכשיו מאזין הקליקים אמור להיראות כך:

LoginFragment.java

...
MaterialButton nextButton = view.findViewById(R.id.next_button);

// Set an error if the password is less than 8 characters.
nextButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
       if (!isPasswordValid(passwordEditText.getText())) {
           passwordTextInput.setError(getString(R.string.shr_error_password));
       } else {
           passwordTextInput.setError(null); // Clear the error
           ((NavigationHost) getActivity()).navigateTo(new ProductGridFragment(), false); // Navigate to the next Fragment
       }
   }
});
...

שורת הקוד החדשה הזו קוראת לשיטה navigateTo() מ-MainActivity כדי לנווט אל מקטע חדש – ProductGridFragment. כרגע זה דף ריק שעליו תעבדו ב-MDC-102.

עכשיו בונים את האפליקציה. לוחצים על הלחצן Next (הבא).

כל הכבוד! המסך הזה יהיה נקודת ההתחלה של ה-codelab הבא שבו תעבדו ב-MDC-102.

6. הפעולה הושלמה

באמצעות תגי עיצוב בסיסיים של XML וכ-30 שורות של Java, ספריית רכיבי Material ל-Android עזרה לכם ליצור דף כניסה יפהפה שתואם להנחיות של Material Design, וגם נראה ומתנהג באופן עקבי בכל המכשירים.

השלבים הבאים

שדה הטקסט והלחצן הם שני רכיבי ליבה בספריית MDC Android, אבל יש עוד הרבה רכיבים! אפשר לעיין בשאר הרכיבים ב-MDC Android. אפשר גם לעבור אל MDC 102: Material Design Structure and Layout כדי לקבל מידע על סרגל האפליקציה העליון, תצוגת כרטיסי מיקום ופריסת הרשת. תודה שניסית את רכיבי Material. אנחנו מקווים שנהניתם מה-Codelab הזה!

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

נכון מאוד נכון אין לי דעה לכאן או לכאן לא נכון לא נכון בכלל

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

נכון מאוד נכון אין לי דעה לכאן או לכאן לא נכון לא נכון בכלל