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. - عندما يستخدم
ViewPagerFragmentStatePagerAdapterللتصفّح في عدد كبير أو غير معروف من الأجزاء، استخدِم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()في فئات المحوِّل المستندة إلى الأجزاء.
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 يستخدم طريقة 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 إحدى طرق حلّ هذه المشكلة باستخدام تنسيق مخصّص للالتفاف متعدد الاستخدامات.
مراجع إضافية
لمزيد من المعلومات حول "ViewPager2"، يُرجى الاطّلاع على المراجع الإضافية التالية.
الفيديوهات
- Turning the Page: Migrating to ViewPager2 (مؤتمر Android Dev Summit لعام 2019)