ה-codelab הזה הוא חלק מהקורס Android Kotlin Fundamentals. כדי להפיק את המרב מהקורס הזה, מומלץ לעבוד על ה-codelabs לפי הסדר. כל ה-codelab של הקורס מפורטים בדף הנחיתה של ה-codelab בנושא יסודות Kotlin ל-Android.
מבוא
בשיעורי Lab קודמים בקורס הזה, השתמשתם בפונקציה findViewById()
כדי לקבל הפניות לתצוגות. כשיש לאפליקציה היררכיות מורכבות של תצוגות, הפעולה findViewById()
יקרה ומאטה את האפליקציה, כי מערכת Android עוברת על ההיררכיה של התצוגות, החל מהשורש, עד שהיא מוצאת את התצוגה הרצויה. למזלנו, יש דרך טובה יותר.
כדי להגדיר נתונים בתצוגות, השתמשתם במשאבי מחרוזות והגדרתם את הנתונים מהפעילות. יעיל יותר אם התצוגה תדע על הנתונים. למזלנו, זה אפשרי.
ב-Codelab הזה תלמדו איך להשתמש ב-data binding כדי לבטל את הצורך ב-findViewById()
. בנוסף, מוסבר איך להשתמש בקשירת נתונים כדי לגשת לנתונים ישירות מתצוגה.
מה שכדאי לדעת
חשוב שתכירו את:
- מהי פעילות ואיך מגדירים פעילות עם פריסה ב-
onCreate()
. - יצירת תצוגת טקסט והגדרת הטקסט שיוצג בתצוגת הטקסט.
- שימוש ב-
findViewById()
כדי לקבל הפניה לתצוגה. - יצירה ועריכה של פריסת XML בסיסית לתצוגה.
מה תלמדו
- איך משתמשים בספריית Data Binding כדי לבטל קריאות לא יעילות ל-
findViewById()
. - איך ניגשים לנתוני האפליקציה ישירות מ-XML.
הפעולות שתבצעו:
- לשנות אפליקציה כך שתשתמש בקשירת נתונים במקום ב-
findViewById()
, ותגשת לנתונים ישירות מקובצי ה-XML של הפריסה.
ב-codelab הזה מתחילים עם האפליקציה AboutMe ומשנים אותה כך שתשתמש בקשירת נתונים. האפליקציה תיראה בדיוק אותו דבר בסיום התהליך.
אלה הפעולות שאפשר לבצע באמצעות האפליקציה 'הכרטיס שלי':
- כשהמשתמש פותח את האפליקציה, מוצגים לו שם, שדה להזנת כינוי, לחצן סיום, תמונה של כוכב וטקסט שאפשר לגלול בו.
- המשתמש יכול להזין כינוי ולהקיש על הלחצן סיום. השדה והלחצן הניתנים לעריכה מוחלפים בתצוגת טקסט שבה מוצג הכינוי שהוזן.
אפשר להשתמש בקוד שיצרתם ב-codelab הקודם, או להוריד את הקוד AboutMeDataBinding-Starter מ-GitHub.
הקוד שכתבתם ב-codelabs הקודמים משתמש בפונקציה findViewById()
כדי לקבל הפניות לתצוגות.
בכל פעם שמשתמשים ב- findViewById()
כדי לחפש תצוגה אחרי שהיא נוצרה או נוצרה מחדש, מערכת Android עוברת על היררכיית התצוגה בזמן הריצה כדי למצוא אותה. אם לאפליקציה יש רק כמה תצוגות, זה לא מהווה בעיה. עם זאת, באפליקציות לייצור יכולות להיות עשרות תצוגות בפריסה, וגם עם העיצוב הכי טוב, יהיו תצוגות מקוננות.
תחשבו על פריסה לינארית שמכילה תצוגת גלילה שמכילה תצוגת טקסט. אם היררכיית התצוגה גדולה או עמוקה, יכול להיות שייקח זמן רב למצוא תצוגה, והדבר עלול להאט את האפליקציה באופן מורגש עבור המשתמש. אפשר לשמור צפיות במשתנים במטמון, אבל עדיין צריך לאתחל משתנה לכל צפייה, בכל מרחב שמות. אם יש הרבה צפיות והרבה פעילויות, גם זה מצטבר.
פתרון אחד הוא ליצור אובייקט שמכיל הפניה לכל תצוגה מפורטת. האובייקט הזה, שנקרא אובייקט Binding
, יכול לשמש את כל האפליקציה. הטכניקה הזו נקראת קישור נתונים. אחרי שיוצרים אובייקט binding לאפליקציה, אפשר לגשת לתצוגות ולנתונים אחרים דרך אובייקט ה-binding, בלי לעבור על היררכיית התצוגות או לחפש את הנתונים.
קישור נתונים מספק את היתרונות הבאים:
- הקוד קצר יותר, קל יותר לקריאה וקל יותר לתחזוקה בהשוואה לקוד שמשתמש ב-
findByView()
. - הנתונים והתצוגות מופרדים באופן ברור. היתרון הזה של קישור נתונים הופך לחשוב יותר ויותר בהמשך הקורס.
- מערכת Android עוברת על היררכיית התצוגה רק פעם אחת כדי לקבל כל תצוגה, וזה קורה במהלך הפעלת האפליקציה, ולא בזמן הריצה כשהמשתמש מקיים אינטראקציה עם האפליקציה.
- מקבלים בטיחות סוגים לגישה לתצוגות. (בטיחות סוגים פירושה שהקומפיילר מאמת את הסוגים במהלך הקומפילציה, ומוצגת שגיאה אם מנסים להקצות משתנה מסוג שגוי).
במשימה הזו מגדירים קשירת נתונים, ומשתמשים בקשירת נתונים כדי להחליף קריאות ל-findViewById()
בקריאות לאובייקט הקשירה.
שלב 1: הפעלת קשירת נתונים
כדי להשתמש בקישור נתונים, צריך להפעיל אותו בקובץ Gradle, כי הוא לא מופעל כברירת מחדל. הסיבה לכך היא שקישור נתונים מאריך את זמן ההידור ועשוי להשפיע על זמן ההפעלה של האפליקציה.
- אם אין לכם את אפליקציית AboutMe מ-codelab קודם, תוכלו לקבל את הקוד AboutMeDataBinding-Starter מ-GitHub. פותחים אותו ב-Android Studio.
- פותחים את הקובץ
build.gradle (Module: app)
. - בתוך הקטע
android
, לפני הסוגר המסולסל הסוגר, מוסיפים קטעdataBinding
ומגדירים אתenabled
ל-true
.
dataBinding {
enabled = true
}
- כשמוצגת בקשה, מסנכרנים את הפרויקט. אם לא מוצגת בקשה, בוחרים באפשרות File > Sync Project with Gradle Files (קובץ > סנכרון הפרויקט עם קובצי Gradle).
- תוכלו להפעיל את האפליקציה, אבל לא תראו שינויים.
שלב 2: משנים את קובץ הפריסה כך שאפשר יהיה להשתמש בו עם קישור נתונים
כדי לעבוד עם קישור נתונים, צריך להוסיף תג <layout>
סביב פריסת ה-XML. כך, מחלקת הבסיס כבר לא קבוצת תצוגה, אלא פריסה שמכילה קבוצות תצוגה ותצוגות. אובייקט הקישור יכול לדעת על הפריסה ועל התצוגות שבה.
- פותחים את הקובץ
activity_main.xml
. - עוברים לכרטיסייה טקסט.
- מוסיפים את התג
<layout></layout>
כתג החיצוני ביותר מסביב לתג<LinearLayout>
.
<layout>
<LinearLayout ... >
...
</LinearLayout>
</layout>
- כדי לתקן את ההזחה של הקוד, בוחרים באפשרות Code > Reformat code.
הצהרות מרחב השמות של פריסת דפים צריכות להיות בתג החיצוני ביותר.
- גוזרים את ההצהרות של מרחבי השמות מהתג
<LinearLayout>
ומדביקים אותן בתג<layout>
. תג הפתיחה<layout>
צריך להיראות כמו בדוגמה שלמטה, ותג הסגירה<LinearLayout>
צריך להכיל רק מאפייני תצוגה.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
- כדי לוודא שהפעולה בוצעה בצורה נכונה, צריך לבנות את האפליקציה ולהפעיל אותה.
שלב 3: יוצרים אובייקט binding בפעילות הראשית
מוסיפים הפניה לאובייקט הקישור לפעילות הראשית, כדי שתוכלו להשתמש בו כדי לגשת לתצוגות:
- פותחים את הקובץ
MainActivity.kt
. - לפני
onCreate()
, ברמה העליונה, יוצרים משתנה לאובייקט הקישור. המשתנה הזה נקרא בדרך כללbinding
.
הקומפיילר יוצר את הסוג שלbinding
, המחלקהActivityMainBinding
, במיוחד עבור הפעילות הראשית הזו. השם נגזר מהשם של קובץ הפריסה, כלומרactivity_main + Binding
.
private lateinit var binding: ActivityMainBinding
- אם מוצגת הנחיה ב-Android Studio, מייבאים את
ActivityMainBinding
. אם לא מוצגת לכם הנחיה, לוחצים עלActivityMainBinding
ומקישים עלAlt+Enter
(או עלOption+Enter
ב-Mac) כדי לייבא את הכיתה החסרה. (מקשי קיצור נוספים מפורטים במאמר מקשי קיצור).
ההצהרהimport
אמורה להיראות כמו ההצהרה שמוצגת בהמשך.
import com.example.android.aboutme.databinding.ActivityMainBinding
לאחר מכן, מחליפים את הפונקציה הנוכחית setContentView()
בהוראה שמבצעת את הפעולות הבאות:
- יוצר את אובייקט הקישור.
- משתמש בפונקציה
setContentView()
מהמחלקהDataBindingUtil
כדי לשייך את הפריסהactivity_main
ל-MainActivity
. הפונקציהsetContentView()
הזו מטפלת גם בהגדרה של חלק מהקישור של הנתונים לתצוגות.
- ב-
onCreate()
, מחליפים את הקריאהsetContentView()
בשורת הקוד הבאה.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
- ייבוא של
DataBindingUtil
import androidx.databinding.DataBindingUtil
שלב 4: משתמשים באובייקט הקישור כדי להחליף את כל הקריאות ל-findViewById()
עכשיו אפשר להחליף את כל הקריאות ל-findViewById()
בהפניות לתצוגות שנמצאות באובייקט הקישור. כשיוצרים את אובייקט הקישור, הקומפיילר יוצר את שמות התצוגות באובייקט הקישור ממזהי התצוגות בפריסה, וממיר אותם ל-camel case. לדוגמה, done_button
הופך ל-doneButton
באובייקט הקישור, nickname_edit
הופך ל-nicknameEdit
ו-nickname_text
הופך ל-nicknameText
.
- ב-
onCreate()
, מחליפים את הקוד שמשתמש ב-findViewById()
כדי למצוא אתdone_button
בקוד שמפנה לכפתור באובייקט הקישור.
מחליפים את הקוד הזה:findViewById<Button>(R.id.
done_button
)
בזה:binding.doneButton
הקוד הסופי להגדרת מאזין הקליקים ב-onCreate()
צריך להיראות כך.
binding.doneButton.setOnClickListener {
addNickname(it)
}
- מבצעים את אותה פעולה לכל הקריאות אל
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
בתוך הפונקציה הזו.
- השדה
nicknameText
דורשString
, והשדהnicknameEdit.text
הואEditable
. כשמשתמשים בקישור נתונים, צריך להמיר אתEditable
ל-String
באופן מפורש.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
- אפשר למחוק את הייבואים שמוצגים באפור.
- משתמשים ב-
apply{}
כדי להפוך את הפונקציה ל-Kotlin.
binding.apply {
nicknameText.text = nicknameEdit.text.toString()
nicknameEdit.visibility = View.GONE
doneButton.visibility = View.GONE
nicknameText.visibility = View.VISIBLE
}
- מריצים את האפליקציה… והיא אמורה להיראות ולפעול בדיוק כמו קודם.
אתם יכולים להשתמש בקשירת נתונים כדי להפוך מחלקת נתונים לזמינה ישירות לתצוגה. השיטה הזו מפשטת את הקוד, והיא שימושית מאוד לטיפול במקרים מורכבים יותר.
בדוגמה הזו, במקום להגדיר את השם והכינוי באמצעות משאבי מחרוזות, יוצרים מחלקת נתונים לשם ולכינוי. משתמשים בקשירת נתונים כדי להפוך את מחלקת הנתונים לזמינה לתצוגה.
שלב 1: יוצרים את סיווג הנתונים MyName
- ב-Android Studio, בספרייה
java
, פותחים את הקובץMyName.kt
. אם הקובץ הזה לא קיים, צריך ליצור קובץ Kotlin חדש ולקרוא לוMyName.kt
. - מגדירים סיווג נתונים לשם ולכינוי. משתמשים במחרוזות ריקות כערכי ברירת המחדל.
data class MyName(var name: String = "", var nickname: String = "")
שלב 2: מוסיפים נתונים לפריסה
בקובץ activity_main.xml
, השם מוגדר כרגע ב-TextView
ממקור מחרוזת. צריך להחליף את ההפניה לשם בהפניה לנתונים במחלקת הנתונים.
- פותחים את
activity_main.xml
בכרטיסייה טקסט. - בחלק העליון של הפריסה, בין התגים
<layout>
ו-<LinearLayout>
, מוסיפים תג<data></data>
. כאן מקשרים את התצוגה לנתונים.
<data>
</data>
בתוך תגי הנתונים, אפשר להצהיר על משתנים בעלי שם שמחזיקים הפניה למחלקה.
- בתוך התג
<data>
, מוסיפים תג<variable>
. - מוסיפים פרמטר
name
כדי לתת למשתנה את השם"myName"
. מוסיפים פרמטרtype
ומגדירים את הסוג לשם מלא של מחלקת הנתוניםMyName
(שם החבילה + שם המשתנה).
<variable
name="myName"
type="com.example.android.aboutme.MyName" />
עכשיו, במקום להשתמש במשאב המחרוזת לשם, אפשר להפנות למשתנה myName
.
- מחליפים את
android:text="@string/name"
בקוד שמופיע למטה.
@={}
היא הנחיה לקבלת הנתונים שמצוינים בתוך הסוגריים המסולסלים.
myName
מפנה למשתנה myName
שהגדרתם קודם, שמפנה למחלקת הנתונים myName
ומאחזר את המאפיין name
מהמחלקה.
android:text="@={myName.name}"
שלב 3: יוצרים את הנתונים
עכשיו יש לכם הפניה לנתונים בקובץ הפריסה. לאחר מכן יוצרים את הנתונים בפועל.
- פותחים את הקובץ
MainActivity.kt
. - מעל
onCreate()
, יוצרים משתנה פרטי, שנקרא גםmyName
לפי המוסכמה. מקצים למשתנה מופע של מחלקת הנתוניםMyName
ומעבירים את השם.
private val myName: MyName = MyName("Aleks Haecky")
- ב-
onCreate()
, מגדירים את הערך של המשתנהmyName
בקובץ הפריסה לערך של המשתנהmyName
שהצהרתם עליו קודם. אי אפשר לגשת למשתנה ישירות ב-XML. צריך לגשת אליו דרך אובייקט הקישור.
binding.myName = myName
- יכול להיות שתופיע שגיאה, כי צריך לרענן את אובייקט הקישור אחרי שמבצעים שינויים. צריך לבנות את האפליקציה, ואז השגיאה תיעלם.
שלב 4: שימוש במחלקת הנתונים לכינוי ב-TextView
השלב האחרון הוא להשתמש בסיווג הנתונים גם לכינוי ב-TextView
.
- פתיחת
activity_main.xml
. - בתצוגת הטקסט
nickname_text
, מוסיפים נכסtext
. אפשר להיעזר בnickname
בכיתת הנתונים, כמו שמוצג בהמשך.
android:text="@={myName.nickname}"
- ב-
ActivityMain
, מחליפים אתnicknameText.text = nicknameEdit.text.toString()
בקוד להגדרת הכינוי במשתנהmyName
.
myName?.nickname = nicknameEdit.text.toString()
אחרי שמגדירים את הכינוי, רוצים שהקוד ירענן את ממשק המשתמש עם הנתונים החדשים. כדי לעשות זאת, צריך לבטל את התוקף של כל ביטויי הקישור כדי שהם ייווצרו מחדש עם הנתונים הנכונים.
- מוסיפים
invalidateAll()
אחרי שמגדירים את הכינוי, כדי שממשק המשתמש יתרענן עם הערך באובייקט הקישור המעודכן.
binding.apply {
myName?.nickname = nicknameEdit.text.toString()
invalidateAll()
...
}
- מפתחים ומריצים את האפליקציה, והיא אמורה לפעול בדיוק כמו קודם.
פרויקט Android Studio: AboutMeDataBinding
שלבים לשימוש בקשירת נתונים כדי להחליף קריאות ל-findViewById()
:
- מפעילים את קישור הנתונים בקטע android בקובץ
build.gradle
:dataBinding { enabled = true }
- משתמשים ב-
<layout>
כתצוגת הבסיס בפריסת ה-XML. - הגדרת משתנה של קישור:
private lateinit var binding: ActivityMainBinding
- יוצרים אובייקט קישור ב-
MainActivity
, ומחליפים אתsetContentView
:binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
- מחליפים את הקריאות ל-
findViewById()
בהפניות לתצוגה באובייקט הקישור. לדוגמה:findViewById<Button>(R.id.done_button) ⇒ binding.doneBu
tton
(בדוגמה, השם של התצוגה נוצר בפורמט CamelCase מתוךid
של התצוגה ב-XML).
שלבים לקישור תצוגות לנתונים:
- יוצרים סיווג נתונים לנתונים.
- מוסיפים בלוק
<data>
בתוך התג<layout>
. - מגדירים
<variable>
עם שם וסוג שהוא מחלקת הנתונים.
<data>
<variable
name="myName"
type="com.example.android.aboutme.MyName" />
</data>
- ב-
MainActivity
, יוצרים משתנה עם מופע של מחלקת הנתונים. לדוגמה:private val myName: MyName = MyName("Aleks Haecky")
- באובייקט הקישור, מגדירים את המשתנה למשתנה שיצרתם זה עתה:
binding.myName = myName
- בקובץ ה-XML, מגדירים את התוכן של התצוגה למשתנה שהגדרתם בבלוק
<data>
. משתמשים בסימון נקודות כדי לגשת לנתונים בתוך מחלקת הנתונים.android:text="@={myName.name}"
קורס ב-Udacity:
מסמכי תיעוד למפתחי Android:
- ספריית Data Binding
- כיתות binding שנוצרו
- איך מתחילים להשתמש בקשירת נתונים
- פריסות וביטויי קשירה
בקטע הזה מפורטות אפשרויות למשימות ביתיות לתלמידים שעובדים על ה-Codelab הזה כחלק מקורס בהנחיית מדריך. המורה צריך:
- אם צריך, מקצים שיעורי בית.
- להסביר לתלמידים איך להגיש מטלות.
- בודקים את שיעורי הבית.
אנשי ההוראה יכולים להשתמש בהצעות האלה כמה שרוצים, ומומלץ להם להקצות כל שיעורי בית אחרים שהם חושבים שמתאימים.
אם אתם עובדים על ה-codelab הזה לבד, אתם יכולים להשתמש במשימות האלה כדי לבדוק את הידע שלכם.
עונים על השאלות הבאות
שאלה 1
למה כדאי למזער קריאות מפורשות ומשתמעות ל-findViewById()
?
- בכל פעם שמתבצעת קריאה ל-
findViewById()
, המערכת עוברת על היררכיית התצוגה. -
findViewById()
פועל ב-thread הראשי או ב-thread של ממשק המשתמש. - השיחות האלה עלולות להאט את ממשק המשתמש.
- הסיכוי שהאפליקציה תקרוס נמוך יותר.
שאלה 2
איך היית מתאר/ת את קישור הנתונים?
לדוגמה, הנה כמה דברים שאפשר לומר על קישור נתונים:
- הרעיון המרכזי מאחורי קשירת נתונים הוא ליצור אובייקט שמקשר בין שני חלקי מידע רחוקים בזמן הקומפילציה, כך שלא צריך לחפש את הנתונים בזמן הריצה.
- האובייקט שמציג את ההתאמות האלה נקרא אובייקט התאמה.
- אובייקט הקישור נוצר על ידי הקומפיילר.
שאלה 3
איזו מהאפשרויות הבאות היא לא יתרון של קישור נתונים?
- הקוד קצר יותר, קל יותר לקריאה וקל יותר לתחזוקה.
- הנתונים והתצוגות מופרדים באופן ברור.
- מערכת Android עוברת על היררכיית התצוגה רק פעם אחת כדי לקבל כל תצוגה.
- התקשרות אל
findViewById()
יוצרת שגיאת קומפילציה. - בטיחות סוגים לגישה לתצוגות.
שאלה 4
מה התפקיד של התג <layout>
?
- עוטפים את תצוגת הבסיס בפריסה.
- נוצרים Binding לכל התצוגות בפריסה.
- הוא מציין את התצוגה ברמה העליונה בפריסת XML שמשתמשת בקשירת נתונים.
- אפשר להשתמש בתג
<data>
בתוך<layout>
כדי לקשור משתנה למחלקת נתונים.
שאלה 5
מהי הדרך הנכונה להפנות לנתונים שמוגדרים בפריסת ה-XML?
android:text="@={myDataClass.property}"
android:text="@={myDataClass}"
android:text="@={myDataClass.property.toString()}"
android:text="@={myDataClass.bound_data.property}"
עוברים לשיעור הבא:
קישורים ל-codelabs אחרים בקורס הזה מופיעים בדף הנחיתה של ה-codelabs בנושא יסודות Android Kotlin.