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