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 تبدیل میکنید،
کلاس آداپتور را بهروز کنید تا کلاس انتزاعی مناسب را بهصورت زیر گسترش دهد:
- وقتی از
ViewPagerبرای صفحهبندی نماها استفاده میشود، ازPagerAdapterاستفاده کنید.RecyclerView.AdapterباViewPager2. - وقتی
ViewPagerازFragmentPagerAdapterبرای صفحهبندی تعداد کمی از قطعات ثابت استفاده میکند، ازFragmentStateAdapterباViewPager2استفاده کنید. - When
ViewPagerusedFragmentStatePagerAdapterto page through a large or unknown number of fragment, useFragmentStateAdapterwithViewPager2.
پارامترهای سازنده
کلاسهای آداپتور مبتنی بر قطعه که از 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، منابع اضافی زیر را ببینید.
نمونهها
- نمونههای ViewPager2 در GitHub
ویدیوها
- Turning the Page: Migrating to ViewPager2 (Android Dev Summit '19)