יצירת רשימות דינמיות באמצעות RecyclerView חלק מ-Android Jetpack.
ה-RecyclerView מאפשר להציג בקלות מערכי נתונים גדולים בצורה יעילה. אתם מספקים את הנתונים ומגדירים איך כל פריט ייראה, והספרייה RecyclerView יוצרת את הרכיבים באופן דינמי כשצריך אותם.
כפי שהשם מרמז, RecyclerView ממחזר את הרכיבים הבודדים האלה. כשפריט יוצא מהמסך בזמן גלילה, RecyclerView לא משמיד את התצוגה שלו. במקום זאת, המחלקה RecyclerView עושה שימוש חוזר בתצוגה עבור פריטים חדשים שנגללו למסך. RecyclerView משפר את הביצועים ואת מהירות התגובה של האפליקציה, ומפחית את צריכת החשמל.
שיעורים מרכזיים
כמה סוגים של קהלים פועלים יחד כדי ליצור את הרשימה הדינמית.
RecyclerView
הואViewGroup
שמכיל את התצוגות המקבילות לנתונים שלכם. הוא בעצמו תצוגה, ולכן מוסיפים אתRecyclerView
לפריסה כמו שמוסיפים כל רכיב אחר בממשק המשתמש.כל רכיב ברשימה מוגדר על ידי אובייקט view holder. כשיוצרים את מחזיק התצוגה, לא משויכים אליו נתונים. אחרי שיוצרים את מחזיק התצוגה,
RecyclerView
קושר אותו לנתונים שלו. כדי להגדיר את מחזיק התצוגה, מרחיבים אתRecyclerView.ViewHolder
.
RecyclerView
מבקש תצוגות ומקשר את התצוגות לנתונים שלהן על ידי קריאה לשיטות במתאם. כדי להגדיר את המתאם, מרחיבים אתRecyclerView.Adapter
.מנהל הפריסות מסדר את הרכיבים השונים ברשימה. אפשר להשתמש באחד ממנהלי הפריסה שסופקו על ידי ספריית RecyclerView, או להגדיר מנהל פריסה משלכם. כל מנהלי הפריסות מבוססים על המחלקה המופשטת
LayoutManager
בספרייה.
אפשר לראות איך כל החלקים משתלבים באפליקציית הדוגמה RecyclerView (Kotlin) או באפליקציית הדוגמה RecyclerView (Java).
שלבים להטמעה של RecyclerView
אם אתם מתכוונים להשתמש ב-RecyclerView, יש כמה דברים שאתם צריכים לעשות. הם מוסברים בפירוט בקטעים הבאים.
בוחרים איך רוצים שהרשימה או הרשת ייראו. בדרך כלל, אפשר להשתמש באחד ממנהלי הפריסה הרגילים של ספריית RecyclerView.
קובעים איך כל רכיב ברשימה ייראה ויתנהג. על סמך העיצוב הזה, הרחב את המחלקה
ViewHolder
. הגרסה שלך שלViewHolder
מספקת את כל הפונקציות לפריטים ברשימה. מחזיק התצוגה הוא wrapper מסביב ל-View
, והתצוגה הזו מנוהלת על ידיRecyclerView
.מגדירים את
Adapter
שמשייך את הנתונים לתצוגות המפורטות שלViewHolder
.
יש גם אפשרויות מתקדמות להתאמה אישית שמאפשרות להתאים את RecyclerView בדיוק לצרכים שלכם.
תכנון הפריסה
הפריטים ב-RecyclerView מסודרים לפי כיתה LayoutManager
. ספריית RecyclerView מספקת שלושה מנהלי פריסה שמטפלים במצבי הפריסה הנפוצים ביותר:
-
LinearLayoutManager
מסדר את הפריטים ברשימה חד-ממדית. -
GridLayoutManager
מסדר את הפריטים ברשת דו-ממדית:- אם הרשת מסודרת אנכית,
GridLayoutManager
מנסה להגדיר לכל הרכיבים בכל שורה רוחב וגובה זהים, אבל יכול להיות שגובה השורות יהיה שונה. - אם הרשת מסודרת אופקית,
GridLayoutManager
מנסה להגדיר לכל הרכיבים בכל עמודה את אותו הרוחב והגובה, אבל עמודות שונות יכולות להיות ברוחב שונה.
- אם הרשת מסודרת אנכית,
-
StaggeredGridLayoutManager
דומה ל-GridLayoutManager
, אבל לא נדרש שפריטים בשורה יהיו באותו הגובה (בפריסות אנכיות) או שפריטים באותה עמודה יהיו באותו הרוחב (בפריסות אופקיות). התוצאה היא שהפריטים בשורה או בעמודה יכולים להיות מוסטים אחד מהשני.
בנוסף, צריך לעצב את הפריסה של כל פריט. תצטרכו את הפריסה הזו כשתיצרו את מחזיק התצוגה, כמו שמתואר בקטע הבא.
הטמעה של המתאם ושל מחזיק התצוגה
אחרי שקובעים את הפריסה, צריך להטמיע את Adapter
ואת ViewHolder
. שתי המחלקות האלה פועלות יחד כדי להגדיר את אופן הצגת הנתונים. ViewHolder
הוא רכיב wrapper של View
שמכיל את הפריסה של פריט בודד ברשימה. Adapter
יוצר אובייקטים של ViewHolder
לפי הצורך, וגם מגדיר את הנתונים לתצוגות האלה. התהליך של שיוך תצוגות לנתונים שלהן נקרא קישור.
כשמגדירים את המתאם, מבטלים את שלוש שיטות המפתח:
onCreateViewHolder()
: RecyclerView
קורא לשיטה הזו בכל פעם שהוא צריך ליצורViewHolder
חדש. השיטה יוצרת ומאתחלת אתViewHolder
ואתView
המשויך, אבל לא ממלאת את התוכן של התצוגה –ViewHolder
עדיין לא קשור לנתונים ספציפיים.
onBindViewHolder()
: RecyclerView
קורא לשיטה הזו כדי לשייךViewHolder
לנתונים. השיטה מאחזרת את הנתונים המתאימים ומשתמשת בהם כדי למלא את פריסת מחזיק התצוגה. לדוגמה, אםRecyclerView
מציג רשימה של שמות, יכול להיות שהשיטה תמצא את השם המתאים ברשימה ותמלא את הווידג'טTextView
של placeholder התצוגה.
getItemCount()
: RecyclerView
קוראת לשיטה הזו כדי לקבל את הגודל של מערך הנתונים. לדוגמה, באפליקציה של פנקס כתובות, זה יכול להיות המספר הכולל של הכתובות. ה-RecyclerView משתמש בזה כדי לקבוע מתי אין יותר פריטים שאפשר להציג.
זוהי דוגמה טיפוסית למתאם פשוט עם ViewHolder
מוטמע שמציג רשימה של נתונים. במקרה הזה, ה-RecyclerView מציג רשימה פשוטה של רכיבי טקסט. המתאם מקבל מערך של מחרוזות שמכילות את הטקסט של רכיבי ViewHolder
.
Kotlin
class CustomAdapter(private val dataSet: Array<String>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() { /** * Provide a reference to the type of views that you are using * (custom ViewHolder) */ class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val textView: TextView init { // Define click listener for the ViewHolder's View textView = view.findViewById(R.id.textView) } } // Create new views (invoked by the layout manager) override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder { // Create a new view, which defines the UI of the list item val view = LayoutInflater.from(viewGroup.context) .inflate(R.layout.text_row_item, viewGroup, false) return ViewHolder(view) } // Replace the contents of a view (invoked by the layout manager) override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { // Get element from your dataset at this position and replace the // contents of the view with that element viewHolder.textView.text = dataSet[position] } // Return the size of your dataset (invoked by the layout manager) override fun getItemCount() = dataSet.size }
Java
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> { private String[] localDataSet; /** * Provide a reference to the type of views that you are using * (custom ViewHolder) */ public static class ViewHolder extends RecyclerView.ViewHolder { private final TextView textView; public ViewHolder(View view) { super(view); // Define click listener for the ViewHolder's View textView = (TextView) view.findViewById(R.id.textView); } public TextView getTextView() { return textView; } } /** * Initialize the dataset of the Adapter * * @param dataSet String[] containing the data to populate views to be used * by RecyclerView */ public CustomAdapter(String[] dataSet) { localDataSet = dataSet; } // Create new views (invoked by the layout manager) @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { // Create a new view, which defines the UI of the list item View view = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.text_row_item, viewGroup, false); return new ViewHolder(view); } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder viewHolder, final int position) { // Get element from your dataset at this position and replace the // contents of the view with that element viewHolder.getTextView().setText(localDataSet[position]); } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return localDataSet.length; } }
הפריסה של כל פריט בתצוגה מוגדרת בקובץ פריסה מסוג XML, כרגיל.
במקרה הזה, לאפליקציה יש קובץ text_row_item.xml
שנראה כך:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/list_item_height"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginRight="@dimen/margin_medium"
android:gravity="center_vertical">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/element_text"/>
</FrameLayout>
השלבים הבאים
בקטע הקוד הבא מוצג אופן השימוש בתג RecyclerView
.
Kotlin
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val dataset = arrayOf("January", "February", "March") val customAdapter = CustomAdapter(dataset) val recyclerView: RecyclerView = findViewById(R.id.recycler_view) recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.adapter = customAdapter } }
Java
RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.layoutManager = new LinearLayoutManager(this) recyclerView.setAdapter(customAdapter);
הספרייה מציעה גם הרבה דרכים להתאים אישית את ההטמעה. מידע נוסף זמין במאמר בנושא התאמה אישית מתקדמת של RecyclerView.
הפעלת תצוגה מקצה לקצה
כדי להפעיל תצוגה מקצה לקצה ב-RecyclerView
:
- כדי להגדיר תצוגה מקצה לקצה שתואמת לדור קודם, מתקשרים אל
enableEdgeToEdge()
. - אם הפריטים ברשימה חופפים בתחילה לסרגלי המערכת, צריך להחיל שוליים פנימיים על
RecyclerView
. אפשר לעשות זאת על ידי הגדרתandroid:fitsSystemWindows
ל-true
או באמצעותViewCompat.setOnApplyWindowInsetsListener
. - כדי לאפשר לשרטט את פריטי הרשימה מתחת לסרגלי המערכת בזמן הגלילה, צריך להגדיר את
android:clipToPadding
לערךfalse
ב-RecyclerView
.
בסרטון הבא מוצג RecyclerView
עם תצוגה מקצה לקצה כשהיא מושבתת (מימין) ומופעלת (משמאל):
קוד לדוגמה של מודעת Inset:
Kotlin
ViewCompat.setOnApplyWindowInsetsListener( findViewById(R.id.my_recycler_view) ) { v, insets -> val innerPadding = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() // If using EditText, also add // "or WindowInsetsCompat.Type.ime()" to // maintain focus when opening the IME ) v.setPadding( innerPadding.left, innerPadding.top, innerPadding.right, innerPadding.bottom) insets }
Java
ViewCompat.setOnApplyWindowInsetsListener( activity.findViewById(R.id.my_recycler_view), (v, insets) -> { Insets innerPadding = insets.getInsets( WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() // If using EditText, also add // "| WindowInsetsCompat.Type.ime()" to // maintain focus when opening the IME ); v.setPadding( innerPadding.left, innerPadding.top, innerPadding.right, innerPadding.bottom ); return insets; } );
קובץ ה-RecyclerView
XML:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent" />
מקורות מידע נוספים
למידע נוסף על בדיקות ב-Android, אפשר לעיין במקורות המידע הבאים.