ViewPager2 היא גרסה משופרת של הספרייה ViewPager שמציעה פונקציונליות משופרת ופותרת קשיים נפוצים בשימוש ב-ViewPager.
אם האפליקציה שלכם כבר משתמשת ב-ViewPager, קראו את הדף הזה כדי ללמוד איך לעבור ל-ViewPager2.
אם אתם רוצים להשתמש ב-ViewPager2 באפליקציה שלכם ואתם לא משתמשים כרגע ב-ViewPager, קראו את המאמרים מעבר בין מקטעים באמצעות ViewPager2 ויצירת תצוגות החלקה עם כרטיסיות באמצעות ViewPager2 כדי לקבל מידע נוסף.
היתרונות של המעבר ל-ViewPager2
הסיבה העיקרית למעבר היא ש-ViewPager2 מקבל תמיכה פעילה בפיתוח, ו-ViewPager לא. עם זאת, ל-ViewPager2 יש גם כמה יתרונות ספציפיים אחרים.
תמיכה בכיוון אנכי
ב-ViewPager2 יש תמיכה בדפדוף אנכי בנוסף לדפדוף האופקי הרגיל. כדי להפעיל דפדוף אנכי ברכיב ViewPager2, מגדירים את המאפיין android:orientation שלו:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:orientation="vertical" />
אפשר גם להגדיר את המאפיין הזה באופן פרוגרמטי באמצעות השיטה setOrientation().
תמיכה בכתיבה מימין לשמאל
ViewPager2 תומך בדפדוף מימין לשמאל (RTL). הדפדוף מימין לשמאל מופעל באופן אוטומטי במקומות המתאימים לפי הלוקאל, אבל אפשר גם להפעיל אותו באופן ידני ברכיב ViewPager2 על ידי הגדרת המאפיין android:layoutDirection שלו:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layoutDirection="rtl" />
אפשר גם להגדיר את המאפיין הזה באופן פרוגרמטי באמצעות השיטה setLayoutDirection().
אוספי קטעים שאפשר לשנות
ViewPager2 תומך בדפדוף באוסף של מקטעים שניתן לשנות, וקורא ל-notifyDatasetChanged() כדי לעדכן את ממשק המשתמש כשהאוסף הבסיסי משתנה.
זה אומר שהאפליקציה יכולה לשנות באופן דינמי את אוסף הפרגמנטים בזמן הריצה, ו-ViewPager2 תציג בצורה נכונה את האוסף ששונה.
DiffUtil
ViewPager2 מבוסס על RecyclerView, ולכן יש לו גישה למחלקת השירות DiffUtil. זה מוביל לכמה יתרונות, אבל בעיקר לכך שאובייקטים של ViewPager2 מנצלים באופן טבעי את האנימציות של שינוי מערך הנתונים מהמחלקה RecyclerView.
איך מעבירים את האפליקציה ל-ViewPager2
כדי לעדכן אובייקטים מסוג ViewPager באפליקציה ל-ViewPager2:
עדכון קובצי פריסה ב-XML
קודם כל, מחליפים את הרכיבים ViewPager בקובצי הפריסה של ה-XML ברכיבים ViewPager2:
<!-- A ViewPager element -->
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- A ViewPager2 element -->
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
עדכון מחלקות של מתאמים
כשמשתמשים ב-ViewPager, צריך להרחיב את מחלקת המתאם שסיפקה דפים חדשים לאובייקט. בהתאם לתרחיש לדוגמה, ViewPager השתמש בשלוש מחלקות מופשטות שונות. ב-ViewPager2 יש רק שתי מחלקות מופשטות.
לכל אובייקט ViewPager שאתם ממירים לאובייקט ViewPager2, מעדכנים את מחלקת המתאם כדי להרחיב את המחלקה המופשטת המתאימה, באופן הבא:
- כַּאֲשֵׁר
ViewPagerמְשׁוּמָשׁPagerAdapterכדי לדפדף בין תצוגות, השתמשRecyclerView.AdapterעִםViewPager2. - אם השתמשתם ב-
ViewPagerב-FragmentPagerAdapterכדי לעבור בין דפים במספר קטן וקבוע של קטעים, השתמשו ב-FragmentStateAdapterעםViewPager2. - כשמשתמשים ב-
ViewPagerב-FragmentStatePagerAdapterכדי לעבור בין דפים של מספר גדול או לא ידוע של קטעים, צריך להשתמש ב-FragmentStateAdapterעםViewPager2.
פרמטרים של בנאי (constructor)
מחלקות של מתאמים שמבוססות על מקטעים ויורשות מ-FragmentPagerAdapter או מ-FragmentStatePagerAdapter תמיד מקבלות אובייקט FragmentManager יחיד כפרמטר של פונקציית ה-constructor. כשמרחיבים את FragmentStateAdapter עבור מחלקת מתאם של ViewPager2, יש את האפשרויות הבאות לפרמטרים של בנאי:
- האובייקט
FragmentActivityאו האובייקטFragmentשבהם נמצא האובייקטViewPager2. ברוב המקרים, זו האפשרות הטובה יותר. - אובייקט
FragmentManagerואובייקטLifecycle.
במחלקות של מתאמים מבוססי-תצוגה שיורשות ישירות מ-RecyclerView.Adapter, אין צורך בפרמטר של פונקציית בנאי.
שיטות לשינוי ברירת המחדל
בנוסף, מחלקות המתאם צריכות לשנות שיטות שונות ב-ViewPager2
בהשוואה ל-ViewPager:
- במקום
getCount(), משנים אתgetItemCount(). חוץ מהשם, השיטה הזו לא השתנתה. - במקום
getItem(), צריך לשנות אתcreateFragment()במחלקות של מתאמים שמבוססים על מקטעים. חשוב לוודא שה-method החדשה שלcreateFragment()תמיד מספקת מופע חדש של מקטע בכל פעם שהפונקציה נקראת, במקום לעשות שימוש חוזר במופעים.
סיכום
לסיכום, כדי להמיר מחלקת מתאם של ViewPager לשימוש עם ViewPager2, צריך לבצע את השינויים הבאים:
- משנים את המחלקה הראשית ל-
RecyclerView.Adapterכדי לדפדף בתצוגות, או ל-FragmentStateAdapterכדי לדפדף בפרגמנטים. - משנים את הפרמטרים של ה-constructor במחלקות של מתאמים שמבוססים על מקטעים.
- לשנות את
getItemCount()במקום אתgetCount(). - צריך לשנות את
createFragment()במקום אתgetItem()במחלקות של מתאמים שמבוססים על מקטעים.
Kotlin
// A simple ViewPager adapter class for paging through fragments class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { override fun getCount(): Int = NUM_PAGES override fun getItem(position: Int): Fragment = ScreenSlidePageFragment() } // An equivalent ViewPager2 adapter class class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { override fun getItemCount(): Int = NUM_PAGES override fun createFragment(position: Int): Fragment = ScreenSlidePageFragment() }
Java
// A simple ViewPager adapter class for paging through fragments public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { public ScreenSlidePagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return new ScreenSlidePageFragment(); } @Override public int getCount() { return NUM_PAGES; } } // An equivalent ViewPager2 adapter class private class ScreenSlidePagerAdapter extends FragmentStateAdapter { public ScreenSlidePagerAdapter(FragmentActivity fa) { super(fa); } @Override public Fragment createFragment(int position) { return new ScreenSlidePageFragment(); } @Override public int getItemCount() { return NUM_PAGES; } }
ארגון מחדש של ממשקי TabLayout
ב-ViewPager2 יש שינויים בשילוב עם TabLayout. אם אתם משתמשים כרגע ב-ViewPager עם אובייקט TabLayout כדי להציג כרטיסיות אופקיות לניווט, אתם צריכים לארגן את הקוד מחדש של אובייקט ה-TabLayout כדי לשלב אותו עם ViewPager2.
הפרדנו את TabLayout מ-ViewPager2, ועכשיו הוא זמין כחלק מרכיבי Material. כדי להשתמש בו, צריך להוסיף את התלות המתאימה לקובץ build.gradle:
מגניב
implementation "com.google.android.material:material:1.1.0-beta01"
Kotlin
implementation("com.google.android.material:material:1.1.0-beta01")
צריך גם לשנות את המיקום של הרכיב TabLayout בהיררכיה של קובץ הפריסה ב-XML. ב-ViewPager, הרכיב TabLayout מוצהר כצאצא של הרכיב ViewPager, אבל ב-ViewPager2, הרכיב TabLayout מוצהר ישירות מעל הרכיב ViewPager2, באותה רמה:
<!-- A ViewPager element with a TabLayout -->
<androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</androidx.viewpager.widget.ViewPager>
<!-- A ViewPager2 element with a TabLayout -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
לבסוף, צריך לעדכן את הקוד שמצרף את האובייקט TabLayout לאובייקט ViewPager. למרות ש-TabLayout משתמש ב-method setupWithViewPager() משלו כדי לשלב עם ViewPager, הוא דורש מכונה של TabLayoutMediator כדי לשלב עם ViewPager2.
האובייקט TabLayoutMediator מטפל גם במשימה של יצירת כותרות הדף לאובייקט TabLayout, מה שאומר שמחלקת המתאם לא צריכה לשנות את getPageTitle():
Kotlin
// Integrating TabLayout with ViewPager class CollectionDemoFragment : Fragment() { ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tabLayout = view.findViewById(R.id.tab_layout) tabLayout.setupWithViewPager(viewPager) } ... } class DemoCollectionPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { override fun getCount(): Int = 4 override fun getPageTitle(position: Int): CharSequence { return "OBJECT ${(position + 1)}" } ... } // Integrating TabLayout with ViewPager2 class CollectionDemoFragment : Fragment() { ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tabLayout = view.findViewById(R.id.tab_layout) TabLayoutMediator(tabLayout, viewPager) { tab, position -> tab.text = "OBJECT ${(position + 1)}" }.attach() } ... }
Java
// Integrating TabLayout with ViewPager public class CollectionDemoFragment extends Fragment { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { TabLayout tabLayout = view.findViewById(R.id.tab_layout); tabLayout.setupWithViewPager(viewPager); } ... } public class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter { ... @Override public int getCount() { return 4; } @Override public CharSequence getPageTitle(int position) { return "OBJECT " + (position + 1); } ... } // Integrating TabLayout with ViewPager2 public class CollectionDemoFragment : Fragment() { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { TabLayout tabLayout = view.findViewById(R.id.tab_layout); new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> tab.setText("OBJECT " + (position + 1)) ).attach(); } ... }
תמיכה ברכיבים מקוננים שניתן לגלול בהם
ViewPager2 לא תומך באופן מובנה בתצוגות גלילה בתצוגות גלילה אחרות, אם לתצוגת הגלילה יש את אותו כיוון כמו לאובייקט ViewPager2 שמכיל אותה. לדוגמה, הגלילה לא תפעל בתצוגת גלילה אנכית בתוך אובייקט ViewPager2 בכיוון אנכי.
כדי לתמוך בתצוגת גלילה בתוך אובייקט ViewPager2 עם אותו כיוון, צריך להפעיל את requestDisallowInterceptTouchEvent() באובייקט ViewPager2 כשרוצים לגלול את הרכיב בתצוגת העץ במקום זאת. בדוגמה של גלילה בתצוגת ViewPager2 בתצוגה מקוננת מוצגת דרך אחת לפתרון הבעיה באמצעות פריסת wrapper מותאמת אישית ורסטילית.
מקורות מידע נוספים
כדי לקבל מידע נוסף על ViewPager2, אפשר לעיין במקורות המידע הבאים.
סרטונים
- Turning the Page: Migrating to ViewPager2 (מפגש של מפתחי Android, 2019)