Android Basic Kotlin 02.4: עקרונות בסיסיים לקישור נתונים

Lab Lab זה הוא חלק מקורס Android Kotlin Fundamentals. כדי להפיק את המקסימום מהקורס הזה, יש לפעול ברצף לפי קודי שיעור ה-Lab. כל שיעורי Lab של הקורסים מופיעים בדף הנחיתה של Lab Kotlin Fundamentals ל-Android Lab.

מבוא

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

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

בשיעור Lab זה, תלמדו איך להשתמש בשיוך נתונים כדי לבטל את הצורך ב-findViewById(). בנוסף, מוסבר איך להשתמש בשיוך נתונים כדי לגשת לנתונים ישירות מתצוגה מפורטת.

דברים שחשוב לדעת

כדאי שתכירו את:

  • מהי פעילות ואיך להגדיר פעילות ב-onCreate().
  • יצירה של תצוגת טקסט והגדרה של הטקסט שמוצג בתצוגת הטקסט.
  • שימוש ב-findViewById() לקבלת הפניה לתצוגה מפורטת.
  • יצירה ועריכה של פריסת XML בסיסית לתצוגה מפורטת.

מה תלמדו

  • איך להשתמש בספריית קישור הנתונים כדי לבטל שיחות לא יעילות אל findViewById().
  • איך לקבל גישה לנתוני אפליקציות ישירות מ-XML.

הפעולות שתבצעו:

  • ניתן לשנות אפליקציה כך שישתמשה בשיוך נתונים במקום ב-findViewById(), ולגשת לנתונים ישירות מקובצי ה-XML לפריסה.

ב-codelab זה מתחילים עם האפליקציה aboutMe ומשנים את האפליקציה לצורך שימוש בשיוך נתונים. האפליקציה תיראה בדיוק כמו שצריך!

הפעולות של אפליקציית aboutMe:

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


תוכלו להשתמש בקוד שיצרתם במעבדת הקוד הקודמת, או להוריד את הקוד אודותMeDataBinding-Starter מ-GitHub.

הקוד שכתבת ב-codelabs קודמים משתמש בפונקציה findViewById() כדי לקבל הפניות לצפיות.

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

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

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

יתרונות השימוש בנתונים הם:

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

במשימה הזו מגדירים קיבוץ נתונים ואתם משתמשים בשיוך נתונים כדי להחליף שיחות ב-findViewById() בשיחות לאובייקט הקישור.

שלב 1: מפעילים את קישור הנתונים

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

  1. אם לא הורדת את אפליקציית MeMe ממעבדת Lab קודמת, אפשר לקבל את הקוד אודותMeDataBinding-Starter מ-GitHub. פותחים אותו ב-Android Studio.
  2. פותחים את הקובץ build.gradle (Module: app).
  3. בתוך הקטע android, לפני סוגר הסוגריים, מוסיפים קטע dataBinding ומגדירים את המאפיין enabled לערך true.
dataBinding {
    enabled = true
}
  1. כשמתבקשים, מסנכרנים את הפרויקט. אם לא מופיעה בקשה, בוחרים באפשרות File> Sync Project with Gradle Files.
  2. ניתן להריץ את האפליקציה, אבל לא יוצגו שינויים.

שלב 2: משנים את קובץ הפריסה כך שניתן יהיה להשתמש בו לקישור נתונים

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

  1. פותחים את הקובץ activity_main.xml.
  2. עוברים אל הכרטיסייה טקסט.
  3. יש להוסיף את <layout></layout> כתג החיצוני ביותר סביב <LinearLayout>.
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. כדי לתקן את כניסת הקוד, צריך לבחור קוד > עיצוב מחדש של הקוד.

    ההצהרות של מרחב השמות של הפריסה חייבות להיות בתג החיצוני ביותר.
  1. יש לחתוך את הצהרות מרחב השמות מ-<LinearLayout> ולהדביק אותן בתג <layout>. התג הפותח <layout> אמור להיראות כפי שמוצג בהמשך, והתג <LinearLayout> צריך להכיל רק את מאפייני התצוגה המפורטת.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
  1. בונים ומפעילים את האפליקציה כדי לוודא שעשיתם זאת בצורה נכונה.

שלב 3: יוצרים אובייקט מחייב בפעילות הראשית

יש להוסיף הפניה לאובייקט המקשר אל הפעילות הראשית כדי שניתן יהיה להשתמש בה לצורך גישה לתצוגות המפורטות:

  1. פותחים את הקובץ MainActivity.kt.
  2. לפני ה-onCreate(), ברמה העליונה, יוצרים משתנה לאובייקט המקושר. משתנה זה נקרא בדרך כלל binding.

    הסוג binding, הכיתה ActivityMainBinding, נוצר על ידי המהדר במיוחד בשביל הפעילות הראשית הזו. השם נגזר מהשם של קובץ הפריסה, כלומר activity_main + Binding.
private lateinit var binding: ActivityMainBinding
  1. אם מוצגת בקשה לעשות זאת ב-Android Studio, מייבאים את ActivityMainBinding. אם לא מוצגת בקשה, לוחצים על ActivityMainBinding ואז על Alt+Enter (Option+Enter ב-Mac) כדי לייבא את הכיתה החסרה. (למידע על מקשי קיצור נוספים, ניתן לעיין בקטע מקשי קיצור.)

    ההצהרה import אמורה להיראות דומה לזו שמוצגת למטה.
import com.example.android.aboutme.databinding.ActivityMainBinding

בשלב הבא, מחליפים את הפונקציה setContentView() הנוכחית בהוראה הבאה:

  • יוצר את האובייקט המחייב.
  • משתמשת בפונקציה setContentView() מהמחלקה DataBindingUtil כדי לשייך את הפריסה activity_main ל-MainActivity. הפונקציה setContentView() מטפלת גם בהגדרה מסוימת של קישור נתונים לתצוגות המפורטות.
  1. בעוד onCreate(), יש להחליף את השיחה ב-setContentView() בשורת הקוד הבאה.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  1. ייבוא DataBindingUtil.
import androidx.databinding.DataBindingUtil

שלב 4: משתמשים באובייקט המחייב כדי להחליף את כל הקריאות ל-findViewById()

כעת ניתן להחליף את כל השיחות ב-findViewById() בהפניות לתצוגות המפורטות שבאובייקט המקושר. כאשר האובייקט המקשר נוצר, המהדר יוצר את שמות התצוגות של אובייקט הקישור ממזהי התצוגות בפריסה, וממיר אותן לאותיות גמל. למשל, done_button הוא doneButton באובייקט הקישור, nickname_edit הופך ל-nicknameEdit ו-nickname_text הופך ל-nicknameText.

  1. בonCreate(), מחליפים את הקוד שנעשה בו שימוש ב-findViewById() כדי למצוא את done_button בקוד שמתייחס ללחצן באובייקט המקושר.

    יש להחליף את הקוד הבא: findViewById<Button>(R.id.done_button)
    בקוד הבא: binding.doneButton

    הקוד הסופי להגדרת האזנה לקליקים ב-onCreate() אמור להיראות כך.
binding.doneButton.setOnClickListener {
   addNickname(it)
}
  1. צריך לחזור על הפעולה בכל השיחות אל findViewById() בפונקציה addNickname().
    יש להחליף את כל המופעים של findViewById<View>(R.id.id_view) בערך binding.idView. עושים זאת באופן הבא:
  • מחיקת ההגדרות של המשתנה editText ושל המשתנים nicknameTextView יחד עם הקריאות ל-findViewById(). כך תקבלו שגיאות.
  • כדי לתקן את השגיאות, צריך להשיג את התצוגות nicknameText, nicknameEdit ו-doneButton מאובייקט binding במקום מהמשתנים (נמחקו).
  • החלפת view.visibility ב-binding.doneButton.visibility. שימוש ב-binding.doneButton במקום ב-view שהועבר מאפשר לשמור על עקביות בקוד.

    התוצאה היא הקוד הבא:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
  • אין שינוי בפונקציונליות. אפשר להסיר את הפרמטר view ולעדכן את כל השימושים ב-view לשימוש בפונקציה binding.doneButton.
  1. המדיניות nicknameText מחייבת String, והערך nicknameEdit.text הוא Editable. בעת שימוש בשיוך נתונים, יש להמיר באופן מפורש את Editable ל-String.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. אפשר למחוק את הייבוא שיובאו באפור.
  2. משכפלים את הפונקציה באמצעות apply{}.
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. בונים את האפליקציה ומפעילים אותה...והיא אמורה להיראות ולעבוד בדיוק כמו קודם.

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

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

שלב 1: יצירת סיווג הנתונים של MyName

  1. ב-Android Studio בספרייה java, פותחים את הקובץ MyName.kt. אם אין לך את הקובץ הזה, עליך ליצור קובץ Kotlin חדש ולקרוא לו MyName.kt.
  2. הגדרת מחלקת נתונים עבור השם והכינוי. יש להשתמש במחרוזות ריקות כערכי ברירת המחדל.
data class MyName(var name: String = "", var nickname: String = "")

שלב 2: הוספת נתונים לפריסה

בקובץ activity_main.xml, השם מוגדר כרגע ב-TextView ממשאב מחרוזת. עליכם להחליף את ההפניה לשם בהפניה לנתונים בקטגוריית הנתונים.

  1. פותחים את activity_main.xml בכרטיסייה טקסט.
  2. בחלק העליון של הפריסה, מוסיפים את התג <layout> ואת התג <LinearLayout>, כדי להוסיף תג <data></data>. כאן תחברו את התצוגה לנתונים.
<data>
  
</data>

בתוך תגי הנתונים אפשר להצהיר על משתנים בעלי שם ששומרים הפניה לכיתה.

  1. בתוך התג <data>, מוסיפים תג <variable>.
  2. צריך להוסיף פרמטר name כדי לתת למשתנה שם בפורמט "myName". מוסיפים פרמטר type ומגדירים את השם לסוג מלא של מחלקת הנתונים של MyName (שם חבילה + שם משתנה).
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />

עכשיו, במקום להשתמש במשאב המחרוזת של השם, אפשר להפנות למשתנה myName.

  1. יש להחליף את android:text="@string/name" בקוד הבא.

@={} היא הוראה לקבל את הנתונים שיש אליהם הפניה בתוך הסוגריים המסולסלים.

myName מפנה למשתנה myName שהגדרתם בעבר, שמפנה לסוג הנתונים myName ומאחזר את הנכס name מהכיתה.

android:text="@={myName.name}"

שלב 3: יוצרים את הנתונים

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

  1. פותחים את הקובץ MainActivity.kt.
  2. מעל onCreate(), יוצרים משתנה פרטי שנקרא גם myName לפי מוסכמה. מקצים את המשתנה למופע של מחלקת הנתונים של MyName, שמעבירה את השם.
private val myName: MyName = MyName("Aleks Haecky")
  1. ב-onCreate(), מגדירים את הערך של המשתנה myName בקובץ הפריסה כערך של המשתנה myName שהצהרתם עליו עכשיו. לא ניתן לגשת למשתנה ישירות ב-XML. צריך לגשת אליו דרך האובייקט המחייב.
binding.myName = myName
  1. פעולה זו עשויה להציג שגיאה מכיוון שעליך לרענן את האובייקט המחייב לאחר ביצוע שינויים. בונים את האפליקציה והשגיאה אמורה להיעלם.

שלב 4: שימוש בקטגוריית הנתונים של הכינוי ב-TextView

השלב האחרון הוא גם להשתמש במחלקת הנתונים של הכינוי ב-TextView.

  1. פתיחת activity_main.xml
  2. בתצוגת הטקסט nickname_text, צריך להוסיף נכס text. יש להפנות ל-nickname במחלקת הנתונים, כפי שמתואר בהמשך.
android:text="@={myName.nickname}"
  1. בActivityMain, מחליפים את
    nicknameText.text = nicknameEdit.text.toString()
    בקוד כדי להגדיר את הכינוי במשתנה myName.
myName?.nickname = nicknameEdit.text.toString()

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

  1. צריך להוסיף את invalidateAll() אחרי הגדרת הכינוי, כדי שממשק המשתמש יתרענן עם הערך שבאובייקט האיגוד המעודכן.
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. בונים את האפליקציה ומפעילים אותה, והיא אמורה לפעול בדיוק כמו קודם.

פרויקט של Android Studio: מידע עלMeDataBinding

כך משתמשים בשיוך נתונים להחלפת שיחות אל findViewById():

  1. הפעלת שיוך נתונים בקטע Android של הקובץ build.gradle:
    dataBinding { enabled = true }
  2. שימוש ב-<layout> כתצוגת הבסיס בפריסת ה-XML.
  3. הגדרה של משתנה מחייב:
    private lateinit var binding: ActivityMainBinding
  4. יצירת אובייקט מחייב ב-MainActivity, במקום setContentView:
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. החלפת השיחות אל findViewById() בהפניות לתצוגה המפורטת של האובייקט השייך. לדוגמה:
    findViewById<Button>(R.id.done_button) ⇒ binding.doneButton
    (בדוגמה, שם התצוגה המפורטת נוצר בתצורת גמל מתוך התצוגה של's id ב-XML).

שלבים להגדרת תצוגות מפורטות של נתונים:

  1. יצירת סיווג נתונים.
  2. מוסיפים בלוק <data> בתוך התג <layout>.
  3. יש להגדיר <variable> עם שם וסוג של סיווג נתונים.
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. ב-MainActivity, יוצרים משתנה עם מופע של מחלקת הנתונים. למשל:
    private val myName: MyName = MyName("Aleks Haecky")
  1. באובייקט המקושר, מגדירים את המשתנה למשתנה שיצרתם הרגע:
    binding.myName = myName
  1. ב-XML, מגדירים את התוכן של התצוגה המפורטת למשתנה שהגדרתם בבלוק <data>. אפשר להשתמש בסימון נקודה כדי לגשת לנתונים שנכללים במחלקת הנתונים.
    android:text="@={myName.name}"

קורס אוניברסיטה:

התיעוד של מפתח Android:

בקטע הזה מפורטות מטלות שיעורי בית אפשריות לתלמידים שעובדים עם קוד Lab הזה, במסגרת קורס בהדרכת מורה. למורה יש אפשרות לבצע את הפעולות הבאות:

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

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

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

מענה על השאלות האלה

שאלה 1

למה ברצונך למזער שיחות הכוללות תוכן בוטה ומרומז ל-findViewById()?

  • בכל פעם שמתבצעת קריאה ל-findViewById(), היא חוצה את היררכיית התצוגה.
  • האפליקציה findViewById() פועלת בשרשור הראשי או בשרשור של ממשק המשתמש.
  • השיחות האלה יכולות להאט את ממשק המשתמש.
  • יש סיכוי נמוך יותר שהאפליקציה קרסה.

שאלה 2

כיצד היית מתאר/ת את קישור הנתונים?

לדוגמה, הנה כמה דברים שאפשר לומר על קישור נתונים:

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

שאלה 3

איזה מיתרונות הבאים אינו יתרון של איגוד נתונים?

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

שאלה 4

מהי הפונקציה של התג <layout>?

  • אתם עוטפים אותו בתצוגת השורש.
  • פרטי הקישור נוצרים עבור כל התצוגות המפורטות בפריסה.
  • היא מציינת את התצוגה ברמה העליונה בפריסת XML שמשתמשת בשיוך נתונים.
  • כדי לקשר משתנה לסיווג נתונים צריך להשתמש בתג <data> בתוך <layout>.

שאלה 5

מהי הדרך הנכונה להפנות לנתוני איגוד בפריסת XML?

  • android:text="@={myDataClass.property}"
  • android:text="@={myDataClass}"
  • android:text="@={myDataClass.property.toString()}"
  • android:text="@={myDataClass.bound_data.property}"

מעבר לשיעור הבא: 3.1: יצירת שבר

קישורים למעבדות אחרות של הקוד בקורס הזה זמינים בדף הנחיתה של Android Kotlin Fundamentals Codelabs.