מעבר מ-ViewPager ל-ViewPager2

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). החלוקה לדפים בפורמט 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, מעדכנים את מחלקת המתאם כדי להרחיב את המחלקה המופשטת המתאימה באופן הבא:

פרמטרים של בונה

סוגי מתאמים מבוססי מקטעים יורשים מ-FragmentPagerAdapter או FragmentStatePagerAdapter תמיד מקבל אובייקט FragmentManager יחיד בתור פרמטר constructor. עם הארכה של FragmentStateAdapter למשך סוג המתאם ViewPager2, יש את האפשרויות הבאות ל-constructor במקום זאת:

  • האובייקט FragmentActivity או האובייקט Fragment שבו נמצא אובייקט ViewPager2. ברוב המקרים זו האפשרות הטובה ביותר.
  • אובייקט FragmentManager ואובייקט Lifecycle.

סוגי מתאמים מבוססי-תצוגה שעוברים בירושה ישירות מ-RecyclerView.Adapter לא דורשים פרמטר של constructor.

שיטות לשינוי מברירת המחדל

סיווגי המתאם צריכים גם לשנות שיטות שונות עבור ViewPager2 מאשר במשך ViewPager:

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

סיכום

לסיכום, כדי להמיר סיווג מתאם ViewPager לשימוש עם ViewPager2, עליך לבצע את השינויים הבאים:

  1. לשנות את מחלקת העל ל-RecyclerView.Adapter כדי לעבור בין תצוגות, או FragmentStateAdapter כדי לעבור בין מקטעים.
  2. שינוי הפרמטרים של ה-constructor בסוגי מתאמים שמבוססים על מקטעים.
  3. שינוי getItemCount() במקום getCount().
  4. ביטול של 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 והוא זמין עכשיו כחלק מ רכיבי החומר. כלומר, כדי להשתמש בו צריך להוסיף את התלות המתאימה בקובץ 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 משתמש ב-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 זמין במקורות המידע הנוספים הבאים.

דוגמיות

סרטונים