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

logo_components_color_2x_web_96dp.png

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

material.io/develop

מהם Material Design ורכיבי Material ל-Android?

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

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

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

מה תפַתחו

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

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

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

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

  • שדה טקסט
  • לחצן

מה צריך להכין

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

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

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

הפעלת Android Studio

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

הורדת אפליקציית ה-Codelab לתחילת הדרך

הורדת אפליקציית Starter

אפליקציית המתחילים נמצאת בספרייה 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 כדי להתקין או לעדכן את הרכיבים האלה ולסנכרן את הפרויקט.

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

בפרויקט צריכה להיות תלות ב-MDC Android support library. התלות הזו אמורה כבר להופיע בקוד לדוגמה שהורדתם, אבל מומלץ לבצע את השלבים הבאים כדי לוודא זאת.

  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 אמורים להופיע ממש מתחתיו.

בואו נסתכל על הקוד. סיפקנו מסגרת ניווט פשוטה 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.

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

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

הוספת ה-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 (סיסמה).

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

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

הוספת ה-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>

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

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

נוסיף שיטה בוליאנית פרטית 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.

עכשיו נוסיף מאזין למקש לסיסמה 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;
}            

עכשיו אפשר לעבור אל רכיב אחר. מעדכנים את 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.

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

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

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

השלבים הבאים

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

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

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

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

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