ViewPager2 نسخه بهبودیافتهای از کتابخانه ViewPager است که
عملکرد بهبودیافتهای ارائه میدهد و مشکلات رایج در استفاده از ViewPager را برطرف میکند.
اگر برنامهتان ازقبل از ViewPager استفاده میکند، این صفحه را بخوانید تا درباره
انتقال به ViewPager2 بیشتر بدانید.
اگر میخواهید از ViewPager2 در برنامهتان استفاده کنید و درحالحاضر از
ViewPager استفاده نمیکنید، برای اطلاعات بیشتر، لغزاندن بین قطعهها بااستفاده از
ViewPager2 و ایجاد نماهای تند کشیدن با
برگهها بااستفاده از ViewPager2 را بخوانید.
مزایای انتقال به ViewPager2
دلیل اصلی انتقال این است که ViewPager2 پشتیبانی توسعه فعال دریافت میکند و ViewPager دریافت نمیکند. بااینحال، ViewPager2 چندین مزیت خاص دیگر نیز ارائه میدهد.
پشتیبانی از جهت عمودی
ViewPager2 علاوهبر صفحهبندی افقی سنتی، از صفحهبندی عمودی نیز پشتیبانی میکند. میتوانید با تنظیم کردن ویژگی android:orientation عنصر ViewPager2، ورقزدن عمودی را برای آن فعال کنید:
<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 تبدیل میکنید،
کلاس آداپتور را بهروز کنید تا کلاس انتزاعی مناسب را بهصورت زیر گسترش دهد:
- وقتی از
ViewPagerبرای صفحهبندی نماها استفاده میشود، ازPagerAdapterاستفاده کنیدRecyclerView.AdapterباViewPager2. - وقتی
ViewPagerازFragmentPagerAdapterبرای صفحهبندی تعداد کمی از قطعات ثابت استفاده میکند، ازFragmentStateAdapterباViewPager2استفاده کنید. - وقتی
ViewPagerازFragmentStatePagerAdapterبرای صفحهبندی تعداد زیادی یا نامشخصی از قطعهها استفاده میکند، ازFragmentStateAdapterباViewPager2استفاده کنید.
پارامترهای سازنده
کلاسهای وفقدهنده مبتنی بر قطعه که از FragmentPagerAdapter یا
FragmentStatePagerAdapter ارثبری میکنند، همیشه یک شیء FragmentManager را
بهعنوان پارامتر سازنده میپذیرند. وقتی FragmentStateAdapter را برای کلاس آداپتور ViewPager2 گسترش میدهید، گزینههای زیر را بهجای پارامترهای سازنده دارید:
- شیء
FragmentActivityیا شیءFragmentکه شیءViewPager2در آن قرار دارد. در بیشتر موارد، این گزینه بهتری است. - یک شیء
FragmentManagerو یک شیءLifecycle.
کلاسهای آداپتور مبتنی بر نما که مستقیماً از RecyclerView.Adapter ارثبری میکنند به پارامتر سازنده نیاز ندارند.
روشهای لغو کردن
کلاسهای آداپتور شما نیز باید روشهای متفاوتی را برای ViewPager2
نسبتبه ViewPager لغو کنند:
- بهجای
getCount()،getItemCount()را لغو کنید. بهجز نام، این روش تغییری نکرده است. - بهجای
getItem()،createFragment()را در کلاسهای وفقدهنده مبتنی بر قطعه لغو کنید. مطمئن شوید که روش جدیدcreateFragment()شما همیشه هر بار که تابع فراخوانی میشود، بهجای استفاده مجدد از نمونهها، یک نمونه قطعه جدید ارائه میدهد.
خلاصه
بهطور خلاصه، برای تبدیل کلاس آداپتور ViewPager برای استفاده با ViewPager2،
باید تغییرات زیر را اعمال کنید:
- کلاس برتر را به
RecyclerView.Adapterبرای صفحهبندی ازطریق نماها، یاFragmentStateAdapterبرای صفحهبندی ازطریق قطعهها تغییر دهید. - پارامترهای سازنده را در کلاسهای آداپتور مبتنی بر قطعه تغییر دهید.
getItemCount()بهجایgetCount()لغو شود.- در کلاسهای آداپتور مبتنی بر قطعه،
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، منابع اضافی زیر را ببینید.
ویدیوها
- Turning the Page: Migrating to ViewPager2 (Android Dev Summit '19)