ย้ายข้อมูลจาก 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 แล้ว โดยอัตโนมัติตามความเหมาะสม โดยอิงตามภาษา แต่คุณสามารถ เปิดใช้การแบ่งหน้า 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 รองรับการแบ่งหน้าผ่านคอลเล็กชัน Fragment ที่แก้ไขได้ การโทร notifyDatasetChanged() เพื่ออัปเดต UI เมื่อคอลเล็กชันที่สำคัญมีการเปลี่ยนแปลง

ซึ่งหมายความว่าแอปสามารถแก้ไขคอลเล็กชัน Fragment แบบไดนามิกได้ที่ และ 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 ใช้ไปแล้ว ทั้งนี้ขึ้นอยู่กับกรณีการใช้งาน คลาสนามธรรมที่แตกต่างกัน 3 คลาส ViewPager2 ใช้คลาสนามธรรมเพียง 2 คลาส

สำหรับออบเจ็กต์ ViewPager แต่ละรายการที่คุณกำลังแปลงเป็นออบเจ็กต์ ViewPager2 อัปเดตคลาสอะแดปเตอร์เพื่อขยายคลาส Abstract ที่เหมาะสมดังนี้

  • เมื่อ ViewPager ใช้ PagerAdapter เพื่อเลื่อนดูหน้าเว็บ ให้ใช้ RecyclerView.Adapter กับ ViewPager2
  • เมื่อ ViewPager ใช้ FragmentPagerAdapter เพื่อเลื่อนดู จำนวนคงที่ของส่วนย่อย ให้ใช้ FragmentStateAdapter กับ ViewPager2
  • เมื่อ ViewPager ใช้ FragmentStatePagerAdapter เพื่อแบ่งหน้า ส่วนย่อยจำนวนมากหรือไม่ทราบจำนวน ให้ใช้ FragmentStateAdapter กับ ViewPager2

พารามิเตอร์ตัวสร้าง

คลาสอะแดปเตอร์ที่อิงตาม Fragment ที่รับช่วงมาจาก FragmentPagerAdapter หรือ FragmentStatePagerAdapter ยอมรับออบเจ็กต์ FragmentManager รายการเดียวเสมอ เป็นพารามิเตอร์ตัวสร้าง เมื่อคุณขยายเวลา FragmentStateAdapter เป็นเวลา ประเภทอะแดปเตอร์ ViewPager2 คุณมีตัวเลือกต่อไปนี้สำหรับตัวสร้าง แทน:

  • ออบเจ็กต์ FragmentActivity หรือออบเจ็กต์ Fragment ที่ มีออบเจ็กต์ ViewPager2 รายการ ในกรณีส่วนใหญ่ ตัวเลือกนี้เหมาะสมกว่า
  • ออบเจ็กต์ FragmentManager และออบเจ็กต์ Lifecycle

คลาสอะแดปเตอร์ที่อิงตามการดูที่รับช่วงมาจาก RecyclerView.Adapter โดยตรง ไม่จำเป็นต้องใช้พารามิเตอร์ตัวสร้าง

วิธีการลบล้าง

คลาสอะแดปเตอร์ยังต้องลบล้างเมธอดต่างๆ สำหรับ ViewPager2 ด้วย เมื่อเทียบกับ ViewPager

  • ลบล้าง getItemCount() แทน getCount() นอกเหนือจากชื่อ วิธีการนี้จะไม่มีการเปลี่ยนแปลง
  • แทนที่ getItem() ให้ลบล้าง createFragment() ในส่วนย่อย คลาสอะแดปเตอร์ใหม่ ตรวจสอบว่าใช้เมธอด createFragment() ใหม่เสมอ จะใส่อินสแตนซ์ส่วนย่อยใหม่ทุกครั้งที่มีการเรียกฟังก์ชัน การนำอินสแตนซ์มาใช้ซ้ำ

สรุป

กล่าวโดยสรุปคือ หากต้องการแปลงคลาสอะแดปเตอร์ ViewPager เพื่อใช้กับ ViewPager2 คุณต้องทำการเปลี่ยนแปลงต่อไปนี้

  1. เปลี่ยน Superclass เป็น RecyclerView.Adapter สำหรับการแบ่งหน้ายอดดู หรือ FragmentStateAdapter สำหรับการแบ่งหน้าส่วนย่อย
  2. เปลี่ยนพารามิเตอร์ตัวสร้างในคลาสอะแดปเตอร์ที่อิงตาม Fragment
  3. ลบล้าง getItemCount() แทน getCount()
  4. ลบล้าง createFragment() แทน getItem() ในอะแดปเตอร์ที่อิงตาม Fragment ใหม่

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 แล้ว และขณะนี้พร้อมให้บริการโดยเป็นส่วนหนึ่งของ ส่วนประกอบของวัสดุ ซึ่งหมายความว่าคุณจะต้องเพิ่ม ทรัพยากร Dependency ที่เหมาะสมสำหรับไฟล์ 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 ตัวอย่างแสดงวิธีการแก้ปัญหานี้ที่ครอบคลุมรอบด้าน เลย์เอาต์ Wrapper ที่กำหนดเอง

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับ ViewPager2 ได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้

ตัวอย่าง

วิดีโอ