انتقال از 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 را به‌عنوان پارامتر سازنده می‌پذیرند. وقتی FragmentStateAdapter را برای کلاس آداپتور ViewPager2 گسترش می‌دهید، گزینه‌های زیر را برای پارامترهای سازنده دارید:

  • شیء FragmentActivity یا شیء Fragment که شیء ViewPager2 در آن قرار دارد. در بیشتر موارد، این گزینه بهتری است.
  • یک شیء FragmentManager و یک شیء Lifecycle.

کلاس‌های آداپتور مبتنی بر نما که مستقیماً از RecyclerView.Adapter ارث‌بری می‌کنند به پارامتر سازنده نیاز ندارند.

روش‌های لغو کردن

کلاس‌های آداپتور شما نیز باید روش‌های متفاوتی را برای ViewPager2 نسبت‌به ViewPager لغو کنند:

  • به‌جای getCount()، getItemCount() را لغو کنید. به‌جز نام، این روش تغییری نکرده است.
  • به‌جای getItem()، createFragment() را در کلاس‌های آداپتور مبتنی بر قطعه لغو کنید. مطمئن شوید که روش جدید createFragment() شما همیشه هر بار که تابع فراخوانی می‌شود، به‌جای استفاده مجدد از نمونه‌ها، یک نمونه قطعه جدید ارائه می‌دهد.

خلاصه

به‌طور خلاصه، برای تبدیل کلاس آداپتور ViewPager برای استفاده با ViewPager2، باید تغییرات زیر را اعمال کنید:

  1. کلاس برتر را به RecyclerView.Adapter برای ورق زدن در نماها، یا FragmentStateAdapter برای ورق زدن در قطعه‌ها تغییر دهید.
  2. پارامترهای سازنده را در کلاس‌های آداپتور مبتنی بر قطعه تغییر دهید.
  3. getItemCount() به‌جای getCount() لغو شود.
  4. در کلاس‌های آداپتور مبتنی بر قطعه، createFragment() را به‌جای getItem() لغو کنید.

کاتلین

// 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()
}

جاوا

// 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"

کاتلین

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 استفاده می‌کند، برای ادغام با ViewPager2 به نمونه‌ای از TabLayoutMediator نیاز دارد.

شیء TabLayoutMediator همچنین وظیفه تولید عنوان‌های صفحه را برای شیء TabLayout برعهده دارد، یعنی کلاس آداپتور نیازی به لغو getPageTitle() ندارد:

کاتلین

// 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()
    }
    ...
}

جاوا

// 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 یکی از راه‌های حل این مشکل را با یک طرح‌بندی پوشاننده سفارشی همه‌کاره نشان می‌دهد.

منابع بیشتر

برای کسب اطلاعات بیشتر درباره ViewPager2، منابع اضافی زیر را ببینید.

نمونه‌ها

ویدیوها