Lab Lab זה הוא חלק מקורס Android Kotlin Fundamentals. כדי להפיק את המקסימום מהקורס הזה, יש לפעול ברצף לפי קודי שיעור ה-Lab. כל שיעורי Lab של הקורסים מופיעים בדף הנחיתה של Lab Kotlin Fundamentals ל-Android Lab.
מבוא
ערכת Lab זו מלמדת כיצד להשתמש ב-RecyclerView
להצגת רשימות של פריטים. בעזרת האפליקציה של מעקב אחר השינה, בסדרה של מעבדות קוד קודמות, אפשר להציג נתונים בצורה יעילה ורב-תכליתית יותר, באמצעות RecyclerView
עם ארכיטקטורה מומלצת.
דברים שחשוב לדעת
כדאי שתכירו את:
- בניית ממשק משתמש בסיסי (UI) באמצעות פעילות, שברים וצפיות.
- לנווט בין מקטעים, ולהשתמש ב-
safeArgs
כדי להעביר נתונים בין מקטעים. - שימוש במודלים של תצוגה מפורטת, צפייה במפעלים לבניית מודלים, טרנספורמציות ו-
LiveData
והצופים שלהם. - יצירה של מסד נתונים מסוג
Room
, יצירת DAO והגדרה של ישויות. - שימוש בשגרת נתונים למשימות במסד הנתונים ובמשימות ארוכות אחרות.
מה תלמדו
- איך להשתמש ב
RecyclerView
עםAdapter
וViewHolder
כדי להציג רשימה של פריטים.
הפעולות שתבצעו:
- כדי להשתמש ב
RecyclerView
להצגת נתוני איכות השינה, עליך לשנות את אפליקציית TrackMySleepquality מהשיעור הקודם.
ב-codelab זה בונים את החלק RecyclerView
באפליקציה שעוקב אחרי איכות השינה. האפליקציה משתמשת במסד הנתונים של Room
כדי לאחסן נתוני שינה לאורך זמן.
באפליקציה למעקב אחר שינה, שני מסכים מיוצגים על ידי מקטעים, כפי שמתואר באיור למטה.
במסך הראשון, שנמצא מימין, יש לחצנים להפעלה ולהפסקה של המעקב. במסך הזה מוצגים גם כל נתוני השינה של המשתמש. לחצן הניקוי מוחק באופן סופי את כל הנתונים שהאפליקציה אספה עבור המשתמש. המסך השני, שמוצג בצד שמאל, הוא בחירה בדירוג של איכות השינה.
האפליקציה הזו משתמשת בארכיטקטורה פשוטה עם בקר ממשק משתמש, ViewModel
ו-LiveData
. האפליקציה משתמשת גם במסד הנתונים Room
כדי להפוך את נתוני השינה לקבועים.
רשימת לילות השינה המוצגת במסך הראשון פעילה, אך לא יפה. האפליקציה משתמשת בתבנית מורכבת כדי ליצור מחרוזות טקסט לתצוגת הטקסט ומספרים לאיכות. כמו כן, העיצוב הזה לא מותאם. לאחר שתתקנו את כל הבעיות האלו ב-codelab הזה, לאפליקציה הסופית תהיה אותה פונקציונליות, והמסך הראשי ייראה כך:
הצגת רשימה או רשת נתונים היא אחת המשימות הנפוצות ביותר בממשק המשתמש ב-Android. הרשימות משתנות מפשוטה ומורכבת מאוד. רשימה של תצוגות טקסט עשויה להציג נתונים פשוטים, כמו רשימת קניות. רשימה מורכבת, כגון רשימה של יעדי חופשה, עשויה להציג למשתמש פרטים רבים בתוך רשת גלילה עם כותרות.
כדי לתמוך בכל תרחישי השימוש האלה, Android מספק את הווידג'ט RecyclerView
.
היתרון הגדול ביותר של RecyclerView
הוא שהוא יעיל מאוד ברשימות גדולות:
- כברירת מחדל,
RecyclerView
פועלת רק כדי לעבד או לצייר פריטים שגלויים כרגע במסך. לדוגמה, אם ברשימה יש אלפי רכיבים, אבל רק 10 רכיבים גלויים,RecyclerView
פועל מספיק כדי לצייר 10 פריטים על המסך. כשהמשתמש גולל,RecyclerView
מבין אילו פריטים חדשים צריכים להופיע במסך ומספיק בדיוק כדי להציג את הפריטים האלה. - כשפריט נגלל מהמסך, תצוגת הפריטים ממוחזרת. כלומר, הפריט מלא בתוכן חדש שנגלל למסך. התנהגות זו של
RecyclerView
חוסכת הרבה זמן עיבוד ועוזרת לרשימות לעבור בצורה גמישה. - כשפריט משתנה, במקום לעדכן את כל הרשימה,
RecyclerView
יכול לעדכן אותו. זהו שיפור משמעותי בביצועים בעת הצגת רשימות של פריטים מורכבים!
ברצף שבהמשך אפשר לראות שתצוגה אחת מלאה בנתונים, ABC
. לאחר שהתצוגה גוללים למסך, האפליקציה RecyclerView
משתמשת שוב בתצוגה של הנתונים החדשים, XYZ
.
דפוס המתאם
אם אתם נוסעים בין מדינות שבהן משתמשים בשקעי חשמל שונים, סביר להניח שאתם יודעים איך לחבר את המכשירים לשקעים באמצעות מתאם. המתאם מאפשר לך להמיר סוג אחד של תקע לסוג אחר, שמגדיר בפועל ממשק אחד לממשק אחר.
תבנית המתאם בהנדסת תוכנה עוזרת לאובייקט לעבוד עם API אחר. RecyclerView
משתמשת במתאם כדי להפוך נתוני אפליקציה לרכיב שנמצא ב-RecyclerView
, בלי לשנות את האופן שבו האפליקציה מאחסנת ומעבדת את הנתונים. באפליקציה למעקב אחר שינה, בונים מתאם שמתאים את הנתונים ממסד הנתונים של Room
למשהו ש-RecyclerView
יודע איך להציג, בלי לשנות את ViewModel
.
הטמעה של RecyclerView
כדי להציג את הנתונים שלך בRecyclerView
, צריך את החלקים הבאים:
- נתונים להצגה.
- מופע של
RecyclerView
המוגדר בקובץ הפריסה כך שיוכל לשמש כמאגר של התצוגות המפורטות. - פריסה של פריט נתונים אחד.
אם כל הפריטים ברשימה נראים זהים, אפשר להשתמש באותה פריסה לכולם. עם זאת, זו לא חובה. את הפריסה של הפריטים צריך ליצור בנפרד מהפריסה של קטעי קוד וקטע אחד, כדי שאפשר יהיה ליצור תצוגת פריטים אחת בכל פעם ולמלא אותה בנתונים. - מנהל פריסה.
מנהל הפריסה מטפל בארגון (הפריסה) של רכיבי ממשק המשתמש בתצוגה מפורטת. - בעל תצוגה.
הבעלים של התצוגה מרחיב את הכיתה מסוגViewHolder
. הוא מכיל את פרטי התצוגה של פריט אחד מהפריסה של הפריט. בנוסף, בעלי התצוגה יכולים להוסיף מידע שמשמש אתRecyclerView
כדי להעביר ביעילות את הצפיות במסך. - מתאם.
המתאם מחבר את הנתונים ל-RecyclerView
. היא מתאימה את הנתונים כך שניתן יהיה להציג אותם ב-ViewHolder
.RecyclerView
משתמש במתאם כדי להבין איך להציג את הנתונים במסך.
במשימה הזו צריך להוסיף RecyclerView
לקובץ הפריסה ולהגדיר Adapter
כדי לחשוף את נתוני השינה למכשיר RecyclerView
.
שלב 1: הוספת RecyclerView עם LayoutManager
בשלב זה, אתם מחליפים את ScrollView
ב-RecyclerView
בקובץ fragment_sleep_tracker.xml
.
- מורידים את האפליקציה RecyclerViewFundamentals-Starter מ-GitHub.
- בונים ומפעילים את האפליקציה. חשוב לשים לב איך הנתונים מוצגים כטקסט פשוט.
- פותחים את קובץ הפריסה
fragment_sleep_tracker.xml
בכרטיסייה עיצוב ב-Android Studio. - בחלונית עץ הרכיבים, מוחקים את
ScrollView
. פעולה זו גם מוחקת אתTextView
בתוךScrollView
. - בחלונית לוח הצבעים, גוללים ברשימה של סוגי הרכיבים שמימין כדי למצוא את מאגרים, ולאחר מכן בוחרים אותם.
- גוררים
RecyclerView
מהחלונית לוח הצבעים אל החלונית עץ הרכיבים. יש למקם אתRecyclerView
בתוךConstraintLayout
.
- אם תיפתח תיבת דו-שיח עם שאלה אם רוצים להוסיף תלות, לוחצים על אישור כדי לאפשר ל-Android Studio להוסיף את התלות של
recyclerview
לקובץ Gradle שלכם. ייתכן שיחלפו כמה שניות עד שהאפליקציה תסונכרן.
- פותחים את קובץ המודול
build.gradle
, גוללים עד הסוף ומחפשים את התלות החדשה, שנראית כמו הקוד הבא:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
- חזרה אל
fragment_sleep_tracker.xml
. - בכרטיסייה Text, מחפשים את הקוד
RecyclerView
שמוצג למטה:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />
RecyclerView
ב-id
מתוךsleep_list
.
android:id="@+id/sleep_list"
- ממקמים את
RecyclerView
כך שיכסה את החלק הנותר של המסך בתוךConstraintLayout
. לשם כך, יש להגביל את החלק העליון שלRecyclerView
ללחצן התחלה, בחלק התחתון של הלחצן ניקוי וכל צד בהורה. יש להגדיר את הרוחב והגובה של הפריסה ל-0dp ב-Layout Editor או ב-XML באמצעות הקוד הבא:
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/clear_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stop_button"
- הוספת מנהל פריסה ל-XML של
RecyclerView
. לכלRecyclerView
יש צורך במנהל פריסה שמסביר לו כיצד למקם פריטים ברשימה. Android מספקLinearLayoutManager
, שמגדיר כברירת מחדל את הפריטים ברשימה אנכית של שורות ברוחב מלא.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- עוברים לכרטיסייה עיצוב ושימו לב שהמגבלות הנוספות גרמו ל
RecyclerView
להתרחב ולמלא את השטח הזמין.
שלב 2: יוצרים את הפריסה של פריט הרשימה ואת הבעלים של תצוגת טקסט
ה-RecyclerView
הוא רק מאגר. בשלב זה, יוצרים את הפריסה ואת התשתית עבור הפריטים שיוצגו בתוך RecyclerView
.
כדי להגיע אל RecyclerView
לעבוד מהר ככל האפשר, בהתחלה משתמשים בפריט רשימה פשוט שמציג את איכות השינה כמספר בלבד. לשם כך, צריך בעל תצוגה, TextItemViewHolder
. נדרשת גם תצוגה מפורטת, TextView
, לנתונים. (בשלב מאוחר יותר, תוכלו לקבל מידע נוסף על בעלי תצוגה ועל אופן הפריסה של כל נתוני השינה).
- יצירת קובץ פריסה בשם
text_item_view.xml
. לא משנה במה אתם משתמשים כרכיב הבסיס, כי אתם מחליפים את קוד התבנית. - ב-
text_item_view.xml
, מוחקים את כל הקוד הנתון. - צריך להוסיף
TextView
עם מרווח פנימי16dp
בהתחלה ובסיום, וגודל טקסט של24sp
. יש לוודא שהרוחב תואם להורה, והגובה עוטף את התוכן. מאחר שהתצוגה הזו מוצגת בתוךRecyclerView
, אין צורך למקם אותה ב-ViewGroup
.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:textSize="24sp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- פתיחת
Util.kt
יש לגלול לסוף ולהוסיף את ההגדרה המוצגת למטה, שיוצרת את הכיתהTextItemViewHolder
. צריך להציב את הקוד בחלק התחתון של הקובץ, אחרי תו הסגירה האחרון. הקוד מופיע בUtil.kt
כי בעל התצוגה המפורטת הזו הוא זמני, והחלפת אותו מאוחר יותר.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
- אם מוצגת בקשה, מייבאים את
android.widget.TextView
ואתandroidx.recyclerview.widget.RecyclerView
.
שלב 3: יצירת SleepNightAdapter
המשימה העיקרית בהטמעת RecyclerView
היא יצירת המתאם. יש לכם בעל תצוגה פשוטה לתצוגת הפריט, ופריסה לכל פריט. עכשיו אפשר ליצור מתאם. המתאם יוצר בעל תצוגה וממלא בו נתונים להצגת RecyclerView
.
- בחבילה של
sleeptracker
, יש ליצור כיתה חדשה מ-Kotlin שנקראתSleepNightAdapter
. - משנים את
SleepNightAdapter
השיעורRecyclerView.Adapter
. הכיתה נקראתSleepNightAdapter
כי היא מתאימה את אובייקטSleepNight
לרכיב ש-RecyclerView
יכול להשתמש בו. המתאם צריך לדעת באיזה בעל תצוגה להשתמש, לכן כדאי לעבור ב-TextItemViewHolder
. מייבאים את הרכיבים הנחוצים כשמוצגת בקשה לעשות זאת, ואז מופיעה הודעת שגיאה כי יש שיטות שחובה להטמיע.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}
- ברמה העליונה של
SleepNightAdapter
, יוצרים משתנה מסוגlistOf
SleepNight
לשמירת הנתונים.
var data = listOf<SleepNight>()
- בשיטה
SleepNightAdapter
, מבטלים אתgetItemCount()
כדי להחזיר את הגודל של רשימת לילות השינה ב-data
. הערךRecyclerView
צריך לדעת כמה פריטים יש למתאם כדי להציג, והוא עושה זאת על ידי התקשרות ל-getItemCount()
.
override fun getItemCount() = data.size
- ב-
SleepNightAdapter
, מבטלים את הפונקציהonBindViewHolder()
, כפי שמוצג למטה.
פונקצייתonBindViewHolder()
נקראת על ידיRecyclerView
כדי להציג את הנתונים של פריט פריט אחד במיקום שצוין. כלומר, לשיטהonBindViewHolder()
יש שני ארגומנטים: בעל תצוגה ומיקום הנתונים לקישור. עבור האפליקציה הזו, הבעלים הואTextItemViewHolder
, והמיקום הוא המיקום ברשימה.
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}
- בתוך
onBindViewHolder()
, יוצרים משתנה של פריט אחד במיקום נתון בנתונים.
val item = data[position]
- לנכס
ViewHolder
שיצרת יש נכס בשםtextView
. בתוךonBindViewHolder()
, מגדירים את ה-text
שלtextView
למספר באיכות השינה. הקוד הזה מציג רק רשימת מספרים, אבל הדוגמה הפשוטה הזו מאפשרת לכם לראות איך המתאם מעביר את הנתונים אל מסך התצוגה ואל המסך.
holder.textView.text = item.sleepQuality.toString()
- ב
SleepNightAdapter
, צריך לבטל וליישם אתonCreateViewHolder()
. התכונה הזו נקראת כאשרRecyclerView
צריך בעל תצוגה כדי לייצג פריט.
הפונקציה הזו מקבלת שני פרמטרים ומחזירהViewHolder
. הפרמטרparent
, שהוא קבוצת הצפיות שמחזיקה את בעל התצוגה, הוא תמידRecyclerView
. נעשה שימוש בפרמטרviewType
כשיש כמה תצוגות מפורטות באותוRecyclerView
. לדוגמה, אם תוסיפו רשימה של צפיות בטקסט, תמונה וסרטון באותוRecyclerView
, הפונקציהonCreateViewHolder()
תצטרך לדעת באיזה סוג תצוגה להשתמש.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}
- ב-
onCreateViewHolder()
, יוצרים מכונה שלLayoutInflater
.
ניפוי הפריסה לפריסה יודעים איך ליצור תצוגות מפורטות מפריסות XML. ה-context
מכיל מידע על האופן שבו ניתן לנפח את התצוגה באופן תקין. במתאם עבור תצוגת מיחזור, אתה תמיד מעביר בהקשר של קבוצת התצוגהparent
, שהיאRecyclerView
.
val layoutInflater = LayoutInflater.from(parent.context)
- באפליקציה
onCreateViewHolder()
, יש ליצור אתview
על ידי שליחת בקשה ל-layoutinflater
לניפוח שלהם.
העברת הפריסה של ה-XML לתצוגה המפורטת, וקבוצתparent
של התצוגה המפורטת לתצוגה המפורטת. הארגומנט השלישי, הבוליאני הואattachToRoot
. הארגומנט הזה צריך להיותfalse
, כיRecyclerView
מוסיף עבורך את הפריט הזה להיררכיית התצוגה כשמגיע הזמן שלו.
val view = layoutInflater
.inflate(R.layout.text_item_view, parent, false) as TextView
- בעוד
onCreateViewHolder()
, יש להחזירTextItemViewHolder
שבוצעה עםview
.
return TextItemViewHolder(view)
- המתאם צריך ליידע את
RecyclerView
כשהdata
השתנה, כי ל-RecyclerView
אין מידע לגבי הנתונים. צריך לדעת רק את בעלי התצוגה שהמתאם מספק לה.
כדי להודיע ל-RecyclerView
מתי הנתונים המוצגים, יש להוסיף רכיב משנה מותאם אישית למשתנהdata
בראש המחלקהSleepNightAdapter
. בכלי ההגדרה, נותנים ל-data
ערך חדש ואז מתקשרים אלnotifyDataSetChanged()
כדי להפעיל מחדש את שרטוט הרשימה עם הנתונים החדשים.
var data = listOf<SleepNight>()
set(value) {
field = value
notifyDataSetChanged()
}
שלב 4: מודיעים ל-Recycler על המתאם
ב-RecyclerView
צריך לדעת על המתאם שישמש לקבלת בעלי התצוגה.
- פתיחת
SleepTrackerFragment.kt
- ב-
onCreateview()
, יוצרים מתאם. יש להזין את הקוד הזה לאחר יצירת מודלViewModel
, ולפני ההצהרהreturn
.
val adapter = SleepNightAdapter()
- משייכים את
adapter
אלRecyclerView
.
binding.sleepList.adapter = adapter
- יש לנקות את הפרויקט ולבנות אותו מחדש כדי לעדכן את האובייקט
binding
.
אם עדיין מופיעות שגיאות בסביבותbinding.sleepList
אוbinding.FragmentSleepTrackerBinding
, יש לבטל את השמירה של קובצי המטמון ולהפעיל אותם מחדש. (יש לבחור באפשרות קובץ > ביטול מטמון / הפעלה מחדש.)
אם האפליקציה פועלת עכשיו, אין שגיאות, אבל לא יוצגו נתונים כשמקישים על התחלה, ואז על עצירה.
שלב 5: שליפת נתונים למתאם
עד עכשיו יש לך מתאם ודרך לקבל נתונים מהמתאם אל RecyclerView
. עכשיו צריך לקבל את הנתונים מהמתאם מה-ViewModel
.
- פתיחת
SleepTrackerViewModel
- מחפשים את המשתנה
nights
, שבו נשמרים כל לילות השינה. הנתונים האלה מוצגים. כדי להגדיר את המשתנהnights
צריך לקרוא ל-getAllNights()
במסד הנתונים. - יש להסיר את
private
מ-nights
, כי עליך ליצור צופה שצריך לגשת למשתנה הזה. ההצהרה אמורה להיראות כך:
val nights = database.getAllNights()
- בחבילה של
database
, פותחים אתSleepDatabaseDao
. - מוצאים את הפונקציה
getAllNights()
. חשוב לשים לב שהפונקציה הזו מחזירה רשימה של ערכיSleepNight
בתורLiveData
. פירוש הדבר הוא שהמשתנהnights
מכילLiveData
שעודכן על ידיRoom
, ואפשר לצפות ב-nights
כדי לדעת מתי הוא משתנה. - פתיחת
SleepTrackerFragment
- ב-
onCreateView()
, מתחת ליצירת ה-adapter
, יוצרים צופה במשתנהnights
.
אם תציינו את קטע הקוד שלviewLifecycleOwner
לציון הבעלים של מחזור החיים, תוכלו לוודא שהצופה הזה יהיה פעיל רק כאשרRecyclerView
מוצג במסך.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
})
- בתוך כל צופה, בכל פעם שמתקבל ערך שאינו Null (עבור
nights
), יש להקצות את הערךdata
של המתאם. זהו הקוד המלא לצופה והגדרת הנתונים:
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.data = it
}
})
- בונים את הקוד ומפעילים אותו.
מספרים באיכות השינה יופיעו כרשימה אם המתאם פועל. צילום המסך שבצד ימין יציג את הסימן 1- אחרי שמקישים על התחלה. צילום המסך בצד שמאל מציג את המספר המעודכן של איכות השינה לאחר שמקישים על עצירה ובוחרים דירוג איכות.
שלב 6: בדיקת אופן השימוש החוזר של בעלי הצפיות
RecyclerView
מיחזור את בעלי התצוגה, כלומר ניתן לעשות בהם שימוש חוזר. בתצוגה שגוללת במסך, RecyclerView
עושה שימוש חוזר בתצוגה של התצוגה שעומדת לגלול למסך.
מאחר שבעלי התצוגה האלה ממוחזרים, יש לוודא ש-onBindViewHolder()
מגדיר או מאפס התאמות אישיות שפריטים קודמים הגדירו בבעלים.
לדוגמה, אפשר להגדיר את הצבע של הטקסט לאדום אצל בעלי צפיות בעלי דירוג איכות נמוך מ-1 או שווה לשינה באיכות נמוכה.
- בכיתה
SleepNightAdapter
, מוסיפים את הקוד הבא בסוףonBindViewHolder()
.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
}
- מריצים את האפליקציה.
- יש להוסיף חלק מנתוני איכות השינה הנמוכים, והמספר אדום.
- כדאי להוסיף דירוגים גבוהים לאיכות שינה עד שיופיע מספר גבוה בצבע אדום על המסך.
כאשרRecyclerView
משתמש שוב בעלי צפייה, בסופו של דבר הוא עושה שימוש חוזר באחד מבעלי התצוגה האדומים, כדי לקבל איכות גבוהה יותר. הדירוג הגבוה מוצג באופן שגוי באדום.
- כדי לתקן את הבעיה הזו, צריך להוסיף הצהרת
else
כדי להגדיר את הצבע לשחור אם האיכות לא נמוכה מאחת או שווה לו.
בשני התנאים הבוטים, כל אחד מהפריטים ישתמש בצבע הטקסט הנכון בכל פריט.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
} else {
// reset
holder.textView.setTextColor(Color.BLACK) // black
}
- מפעילים את האפליקציה והמספרים צריכים להיות תמיד בצבע הנכון.
מעולה! עכשיו יש לך RecyclerView
עם פונקציונליות מלאה.
במשימה הזו מחליפים את בעל התצוגה הפשוטה, שיש לו נתונים נוספים בלילה של שינה.
הViewHolder
הפשוט שהוספת אל Util.kt
מכסה רק TextView
בTextItemViewHolder
.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
למה RecyclerView
לא משתמש/ת ישירות בTextView
? שורת הקוד הזו מספקת פונקציונליות רבה. ViewHolder
מציין תצוגת פריטים ומטא-נתונים לגבי המקום שלו בתוך RecyclerView
. האפליקציה RecyclerView
מסתמכת על הפונקציונליות הזו כדי למקם בצורה נכונה את התצוגה בזמן הגלילה ברשימה, וכדי לבצע פעולות מעניינות כמו תצוגות מונפשות כשמוסיפים או מסירים פריטים ב-Adapter
.
אם ל-RecyclerView
אין צורך לגשת לתצוגות השמורות ב-ViewHolder
, אפשר לעשות זאת באמצעות נכס itemView
של בעל התצוגה המפורטת. האפליקציה RecyclerView
משתמשת ב-itemView
כאשר היא מקשרת פריט להצגה במסך, בעת ציור קישוטים מסביב לתצוגה כמו גבול, וכדי להטמיע נגישות.
שלב 1: יוצרים את פריסת הפריט
בשלב זה, יוצרים את קובץ הפריסה של פריט אחד. הפריסה כוללת ConstraintLayout
עם ImageView
לאיכות השינה, TextView
למשך השינה ו-TextView
לאיכות של הטקסט. מכיוון שכבר ביצעת פריסות, יש להעתיק ולהדביק את קוד ה-XML שסופק.
- יצירת קובץ משאב חדש לפריסה והענקת לו שם
list_item_sleep_night
. - יש להחליף את כל הקוד שבקובץ בקוד שבהמשך. לאחר מכן תצטרכו להכיר את הפריסה שיצרתם.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content">
<ImageView
android:id="@+id/quality_image"
android:layout_width="@dimen/icon_size"
android:layout_height="60dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_sleep_5" />
<TextView
android:id="@+id/sleep_length"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/quality_image"
app:layout_constraintTop_toTopOf="@+id/quality_image"
tools:text="Wednesday" />
<TextView
android:id="@+id/quality_string"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="@+id/sleep_length"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/sleep_length"
app:layout_constraintTop_toBottomOf="@+id/sleep_length"
tools:text="Excellent!!!" />
</androidx.constraintlayout.widget.ConstraintLayout>
- עוברים לכרטיסייה עיצוב ב-Android Studio. בתצוגת העיצוב, הפריסה נראית כמו צילום המסך שמימין. בתצוגת השרטוט, היא נראית כמו צילום המסך שמשמאל.
שלב 2: יצירת HoldViewer
- פתיחת
SleepNightAdapter.kt
- יש ליצור כיתה במסגרת
SleepNightAdapter
שנקראתViewHolder
ולהאריך אותה ב-RecyclerView.ViewHolder
.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
- בתוך
ViewHolder
, קבלת הפניות לצפיות. נדרשת הפניה לתצוגות המפורטות שViewHolder
יעדכן. בכל פעם שתקשרו את ה-ViewHolder
הזה, עליך לגשת לתמונה ולתצוגות הטקסט. (יש להמיר את הקוד הזה כדי להשתמש בשיוך נתונים מאוחר יותר).
val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
val quality: TextView = itemView.findViewById(R.id.quality_string)
val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)
שלב 3: שימוש ב-ViewHolder ב-SleepNightAdapter
- בהגדרה
SleepNightAdapter
, במקום ב-TextItemViewHolder
, יש להשתמש ב-SleepNightAdapter.ViewHolder
שיצרת.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {
עדכון onCreateViewHolder()
:
- יש לשנות את החתימה של
onCreateViewHolder()
כדי להחזיר אתViewHolder
. - יש לשנות את תכונות הניפוי של הפריסה כדי להשתמש במשאב הפריסה הנכון,
list_item_sleep_night
. - יש להסיר את ההעברה אל
TextView
. - במקום להחזיר
TextItemViewHolder
, יש להחזירViewHolder
.
הנה הפונקציהonCreateViewHolder()
שהסתיימה:
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater =
LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night,
parent, false)
return ViewHolder(view)
}
עדכון onBindViewHolder()
:
- יש לשנות את החתימה של
onBindViewHolder()
כך שהפרמטרholder
יהיהViewHolder
במקוםTextItemViewHolder
. - בתוך
onBindViewHolder()
, יש למחוק את כל הקוד, מלבד ההגדרה שלitem
. - יש להגדיר
val
res
שכולל הפניה אלresources
לתצוגה המפורטת הזו.
val res = holder.itemView.context.resources
- מגדירים את הטקסט של תצוגת הטקסט
sleepLength
בהתאם למשך הזמן. מעתיקים את הקוד שבהמשך, שמפעיל פונקציית פורמט שכלולה בקוד למתחילים.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
- זוהי שגיאה כי יש להגדיר את
convertDurationToFormatted()
. יש לפתוח אתUtil.kt
ולבטל את הוספת הקוד והייבוא המשויך אליו. (בוחרים קוד > הערה עם הערות שורה.) - בחזרה בעוד
onBindViewHolder()
, יש להשתמש ב-convertNumericQualityToString()
כדי להגדיר את האיכות.
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
- ייתכן שתצטרכו לייבא את הפונקציות האלה באופן ידני.
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString
- יש להגדיר את הסמל הנכון לאיכות. הסמל החדש
ic_sleep_active
נמסר לך בקוד למתחילים.
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
- הנה הפונקציה
onBindViewHolder()
המעודכנת המעודכנת, שמגדירה את כל הנתונים עבורViewHolder
:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = data[position]
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}
- מריצים את האפליקציה. המסך צריך להיראות כמו צילום המסך שבהמשך, ומציג את הסמל של איכות השינה, יחד עם טקסט למשך השינה ואיכות השינה.
ה-RecyclerView
שלך הושלם! למדת איך להטמיע Adapter
וViewHolder
, והרכיבתם אותם כדי להציג רשימה עם RecyclerView
Adapter
.
הקוד מציג עד עכשיו את התהליך של יצירת מתאם ובעל תצוגה. עם זאת, אפשר לשפר את הקוד הזה. הקוד שמוצג והקוד לניהול בעלי התצוגה מבלבל, ו-onBindViewHolder()
יודע איך לעדכן את ViewHolder
.
באפליקציית ייצור יכולים להיות לכם כמה בעלי תצוגות, מתאמים מורכבים יותר ומפתחים מרובים שמבצעים שינויים. יש לבנות את הקוד כך שכל מה שקשור לבעל תצוגה יופיע רק בבעל התצוגה.
שלב 1: העברה מחדש של onBindViewHolder()
בשלב זה, מוסיפים מחדש את הקוד ומעבירים את כל הפונקציונליות של בעלי התצוגה המפורטת ל-ViewHolder
. מטרת החישוב מחדש היא לא לשנות את מראה האפליקציה למשתמש, אלא להקל על מפתחים לעבוד על הקוד. למזלנו, ל-Android Studio יש כלים שיעזרו לכם.
- ב-
SleepNightAdapter
, ב-onBindViewHolder()
צריך לבחור הכול מלבד ההצהרה כדי להצהיר על המשתנהitem
. - לוחצים לחיצה ימנית ובוחרים באפשרות refactor > חילוץ > Function.
- נותנים שם לפונקציה
bind
ומאשרים את הפרמטרים המוצעים. לוחצים על אישור.
הפונקציהbind()
ממוקמת מתחת ל-onBindViewHolder()
.
private fun bind(holder: ViewHolder, item: SleepNight) {
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}
- העברת הסמן על המילה
holder
של הפרמטרholder
ב-bind()
. יש ללחוץ עלAlt+Enter
(Option+Enter
ב-Mac) כדי לפתוח את תפריט הכוונה. בוחרים באפשרות ממיר פרמטר לנמען כדי להמיר אותו לפונקציית תוסף עם החתימה הבאה:
private fun ViewHolder.bind(item: SleepNight) {...}
- יש לחתוך ולהדביק את הפונקציה
bind()
ב-ViewHolder
. - הפיכת
bind()
לגלוי לכול. - אם יש צורך, מייבאים את
bind()
למתאם. - מכיוון שעכשיו היא נמצאת ב
ViewHolder
, יש לך אפשרות להסיר את החלק שלViewHolder
מהחתימה. זהו הקוד הסופי של הפונקציהbind()
במחלקהViewHolder
.
fun bind(item: SleepNight) {
val res = itemView.context.resources
sleepLength.text = convertDurationToFormatted(
item.startTimeMilli, item.endTimeMilli, res)
quality.text = convertNumericQualityToString(
item.sleepQuality, res)
qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}
שלב 2: גורם מחדש ב-CreateViewHolder
השיטה onCreateViewHolder()
במתאם מנפחת כרגע את התצוגה ממשאב הפריסה של ViewHolder
. עם זאת, האינפלציה לא קשורה למתאם ולכל מה שקשור לViewHolder
. הניפוח אמור להתבצע ב-ViewHolder
.
- ב-
onCreateViewHolder()
, בוחרים את כל הקוד בגוף הפונקציה. - לוחצים לחיצה ימנית ובוחרים באפשרות refactor > חילוץ > Function.
- נותנים שם לפונקציה
from
ומאשרים את הפרמטרים המוצעים. לוחצים על אישור. - מציבים את הסמן על שם הפונקציה
from
. יש ללחוץ עלAlt+Enter
(Option+Enter
ב-Mac) כדי לפתוח את תפריט הכוונה. - בוחרים העברה לאובייקט נלווה. הפונקציה
from()
צריכה להיות באובייקט נלווה כדי שניתן יהיה לקרוא אותה ברמהViewHolder
, ולא במופע ב-ViewHolder
. - העברת האובייקט
companion
לכיתהViewHolder
. - הפיכת
from()
לגלוי לכול. - ב-
onCreateViewHolder()
, אפשר לשנות את ההצהרהreturn
כדי להחזיר את התוצאה של קריאה ל-from()
בכיתתViewHolder
.
השיטות המלאותonCreateViewHolder()
ו-from()
צריכות להיראות כמו הקוד הבא, והקוד שלך צריך לבנות ולפעול ללא שגיאות.
override fun onCreateViewHolder(parent: ViewGroup, viewType:
Int): ViewHolder {
return ViewHolder.from(parent)
}
companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night, parent, false)
return ViewHolder(view)
}
}
- יש לשנות את החתימה של הכיתה
ViewHolder
כדי שהבונה יהיה פרטי. כי ל-from()
יש עכשיו שיטה שמחזירה מופע חדש שלViewHolder
, לכן אין סיבה להתקשר יותר ל-constructor שלViewHolder
.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
- מריצים את האפליקציה. האפליקציה אמורה לבנות ולהפעיל את אותה פעולה כמו קודם לכן – התוצאה הרצויה אחרי החישוב מחדש.
פרויקט ב-Android Studio: RecyclerViewFundamentals
- הצגת רשימה או רשת נתונים היא אחת המשימות הנפוצות ביותר בממשק המשתמש ב-Android. האפליקציה
RecyclerView
נועדה להיות יעילה גם כשמציגים רשימות גדולות במיוחד. RecyclerView
מבצעת רק את העבודה הנדרשת כדי לעבד או לצייר פריטים שגלויים כרגע במסך.- כשפריט נגלל מהמסך, הצפיות שלו ממוחזרות. כלומר, הפריט מלא בתוכן חדש שנגלל למסך.
- תבנית המתאם בהנדסת תוכנה עוזרת לאובייקט לעבוד יחד עם API אחר.
RecyclerView
משתמשת במתאם כדי להפוך נתוני אפליקציה למשהו שניתן להציג, בלי לשנות את האופן שבו האפליקציה מאחסנת ומעבדת נתונים.
כדי להציג את הנתונים שלך בRecyclerView
, צריך את החלקים הבאים:
- RecyclerView
כדי ליצור מופע שלRecyclerView
, צריך להגדיר רכיב<RecyclerView>
בקובץ הפריסה. - Layout Manager
RecyclerView
משתמש ב-LayoutManager
כדי לארגן את פריסת הפריטים ב-RecyclerView
, כמו למשל פריסת הרשת או רשימה לינארית.
ב-<RecyclerView>
בקובץ הפריסה, מגדירים את המאפייןapp:layoutManager
כמנהל הפריסה (למשל,LinearLayoutManager
אוGridLayoutManager
).
אפשר להגדיר אתLayoutManager
גם עבורRecyclerView
באופן פרוגרמטי. (שיטה זו תקפה במעבדה מאוחרת יותר של קוד Lab.) - פריסה לכל פריט
אפשר ליצור פריסה הכוללת פריט נתונים אחד בקובץ XML. - מתאם
יש ליצור מתאם שמכין את הנתונים ואת האופן שבו הם יוצגו בViewHolder
. משייכים את המתאם אלRecyclerView
.
כאשרRecyclerView
פועל, הוא ישתמש במתאם כדי להבין איך להציג את הנתונים במסך.
המתאם דורש ממך ליישם את השיטות הבאות:
–getItemCount()
כדי להחזיר את מספר הפריטים.
–onCreateViewHolder()
כדי להחזיר אתViewHolder
עבור פריט ברשימה.
–onBindViewHolder()
כדי להתאים את הנתונים לצפיות של פריט ברשימה. - ViewHolder
פריטViewHolder
כולל את פרטי התצוגה של פריט אחד מהפריסה של הפריט. - השיטה
onBindViewHolder()
במתאם מתאימה את הנתונים לתצוגות המפורטות. תמיד אפשר לבטל את השיטה הזו. בדרך כלל,onBindViewHolder()
מנפח את הפריסה של פריט, ומעביר את הנתונים לתצוגות בפריסה. - ל
RecyclerView
אין מידע לגבי הנתונים, ולכןAdapter
צריך להודיע לRecyclerView
על שינוי הנתונים. יש להשתמש ב-notifyDataSetChanged()
כדי להודיע ל-Adapter
שהנתונים השתנו.
קורס אוניברסיטה:
התיעוד של מפתח Android:
בקטע הזה מפורטות מטלות שיעורי בית אפשריות לתלמידים שעובדים עם קוד Lab הזה, במסגרת קורס בהדרכת מורה. למורה יש אפשרות לבצע את הפעולות הבאות:
- אם צריך, מקצים שיעורי בית.
- ספרו לתלמידים איך מגישים מטלות בשיעורי בית.
- לתת ציונים למטלות שיעורי הבית.
המורים יכולים להשתמש בהצעות האלה כמה שפחות, ומומלץ להקצות להן כל שיעורי בית שדעתם מתאימה להם.
אם אתם עובדים בעצמכם על שיעור הקוד הזה, אתם מוזמנים להשתמש במטלות שיעורי הבית האלה כדי לבחון את הידע שלכם.
מענה על השאלות האלה
שאלה 1
איך RecyclerView
מציג פריטים? יש לבחור בכל האפשרויות הרלוונטיות.
▬ הצגת פריטים ברשימה או ברשת.
הוספה של גלילה אנכית או אופקית.
▬ גלילה באלכסון במכשירים גדולים יותר, כמו טאבלטים.
עלות אפשר להשתמש בפריסות בהתאמה אישית במקרים שבהם רשימה או רשת לא מספיקים לשימוש.
שאלה 2
מהם היתרונות של השימוש ב-RecyclerView
? יש לבחור בכל האפשרויות הרלוונטיות.
הוספה ביעילות מוצגות רשימות גדולות.
▢ עדכון הנתונים באופן אוטומטי.
▸ מפחית את הצורך ברענון כשפריט מתעדכן, נמחק או נוסף לרשימה.
▬ שימוש חוזר בתצוגה שגוללת במסך כדי להציג את הפריט הבא שגולל במסך.
שאלה 3
מהן כמה מהסיבות לשימוש במתאמים? יש לבחור בכל האפשרויות הרלוונטיות.
▬ הפרדת חששות מאפשרת לשנות ולבדוק בקלות את הקוד.
הוספה RecyclerView
היא נתון בלתי מוגבל לנתונים המוצגים.
הוספה של שכבות עיבוד נתונים אין צורך לדאוג לגבי האופן שבו הנתונים יוצגו.
▬ האפליקציה תפעל מהר יותר.
שאלה 4
איזו מהאפשרויות הבאות נכונה לגבי ViewHolder
? יש לבחור בכל האפשרויות הרלוונטיות.
▦ הפריסה של ViewHolder
מוגדרת בקובצי פריסה של XML.
▬ יש ViewHolder
לכל יחידת נתונים במערך הנתונים.
הוספה יכולה להיות לך יותר ממדינה אחת (ViewHolder
) בRecyclerView
.
▬ Adapter
מקשרים נתונים אל ViewHolder
.
מעבר לשיעור הבא: