Di chuyển từ ViewPager sang ViewPager2

ViewPager2 là phiên bản cải tiến của thư viện ViewPager cung cấp chức năng nâng cao và giải quyết những khó khăn thường gặp khi sử dụng ViewPager. Nếu ứng dụng của bạn đã sử dụng ViewPager, hãy đọc trang này để tìm hiểu thêm về chuyển sang ViewPager2.

Nếu bạn muốn sử dụng ViewPager2 trong ứng dụng và hiện không sử dụng ViewPager, đọc Trang trình bày giữa các mảnh bằng ViewPager2Tạo thành phần hiển thị vuốt bằng các thẻ bằng ViewPager2 để tìm hiểu thêm của bạn.

Lợi ích của việc di chuyển sang ViewPager2

Lý do chính để di chuyển là vì ViewPager2 đang nhận dữ liệu đang hoạt động hỗ trợ phát triển còn ViewPager thì không. Tuy nhiên, ViewPager2 cũng cung cấp một số lợi thế cụ thể khác.

Hỗ trợ hướng dọc

ViewPager2 hỗ trợ chế độ phân trang theo chiều dọc bên cạnh chế độ phân trang ngang truyền thống phân trang. Bạn có thể bật chế độ phân trang theo chiều dọc cho phần tử ViewPager2 bằng cách đặt Thuộc tính android:orientation:

<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:orientation="vertical" />

Bạn cũng có thể đặt thuộc tính này theo phương thức lập trình bằng cách sử dụng setOrientation() .

Hỗ trợ từ phải sang trái

ViewPager2 hỗ trợ tính năng phân trang từ phải sang trái (RTL). Chế độ phân trang RTL đang bật tự động khi thích hợp dựa trên ngôn ngữ. Tuy nhiên, bạn cũng có thể làm theo cách thủ công bật tính năng phân trang RTL cho phần tử ViewPager2 bằng cách đặt Thuộc tính android:layoutDirection:

<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layoutDirection="rtl" />

Bạn cũng có thể đặt thuộc tính này theo phương thức lập trình bằng cách sử dụng setLayoutDirection() .

Tập hợp mảnh có thể sửa đổi

ViewPager2 hỗ trợ tính năng phân trang thông qua một tập hợp các mảnh có thể sửa đổi, đang gọi notifyDatasetChanged() để cập nhật giao diện người dùng khi bộ sưu tập cơ bản thay đổi.

Điều này có nghĩa là ứng dụng của bạn có thể linh động sửa đổi bộ sưu tập mảnh tại thời gian chạy và ViewPager2 sẽ hiển thị chính xác tập hợp đã sửa đổi.

Khuếch tán

ViewPager2 được xây dựng trên RecyclerView, có nghĩa là ứng dụng này có quyền truy cập vào Phần mềm tiện ích DiffUtil . Điều này mang lại một số lợi ích, nhưng đáng chú ý nhất là Các đối tượng ViewPager2 tận dụng sẵn ảnh động thay đổi tập dữ liệu từ lớp RecyclerView.

Di chuyển ứng dụng sang ViewPager2

Hãy làm theo các bước sau để cập nhật các đối tượng ViewPager trong ứng dụng thành ViewPager2:

Cập nhật tệp bố cục XML

Trước tiên, hãy thay thế các phần tử ViewPager trong tệp bố cục XML bằng Phần tử 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" />

Cập nhật các lớp của bộ chuyển đổi

Khi sử dụng ViewPager, bạn phải mở rộng lớp bộ chuyển đổi đã cung cấp các trang mới cho đối tượng. Tuỳ thuộc vào trường hợp sử dụng, ViewPager đã được sử dụng ba lớp trừu tượng khác nhau. ViewPager2 chỉ sử dụng 2 lớp trừu tượng.

Đối với mỗi đối tượng ViewPager mà bạn đang chuyển đổi thành đối tượng ViewPager2, cập nhật lớp bộ chuyển đổi để mở rộng lớp trừu tượng thích hợp như sau:

Tham số hàm khởi tạo

Các lớp bộ chuyển đổi dựa trên mảnh kế thừa từ FragmentPagerAdapter hoặc FragmentStatePagerAdapter luôn chấp nhận một đối tượng FragmentManager duy nhất làm tham số hàm khởi tạo. Khi bạn mở rộng FragmentStateAdapter cho một Lớp bộ chuyển đổi ViewPager2, bạn có các lựa chọn sau đây cho hàm khởi tạo các tham số thay thế:

  • Đối tượng FragmentActivity hoặc đối tượng Fragment mà trong đó Đối tượng ViewPager2 nằm. Trong hầu hết các trường hợp, đây là lựa chọn tốt hơn.
  • Một đối tượng FragmentManager và một đối tượng Lifecycle.

Các lớp bộ chuyển đổi dựa trên khung hiển thị kế thừa trực tiếp từ RecyclerView.Adapter không yêu cầu tham số hàm khởi tạo.

Phương thức ghi đè

Các lớp bộ chuyển đổi của bạn cũng cần ghi đè các phương thức khác nhau cho ViewPager2 so với thời điểm ViewPager thực hiện:

  • Thay vì getCount(), hãy ghi đè getItemCount(). Ngoài tên, thì phương thức này sẽ không thay đổi.
  • Thay vì getItem(), hãy ghi đè createFragment() theo mảnh các lớp của trình chuyển đổi. Đảm bảo phương thức createFragment() mới của bạn luôn luôn cung cấp một thực thể mảnh mới mỗi khi hàm được gọi thay vì sử dụng lại các bản sao.

Tóm tắt

Tóm lại, để chuyển đổi một lớp trình chuyển đổi ViewPager để sử dụng với ViewPager2, bạn phải thực hiện các thay đổi sau:

  1. Thay đổi lớp cấp cao thành RecyclerView.Adapter để phân trang thông qua các khung hiển thị hoặc FragmentStateAdapter để phân trang qua các phân đoạn.
  2. Thay đổi tham số hàm khởi tạo trong các lớp bộ chuyển đổi dựa trên mảnh.
  3. Ghi đè getItemCount() thay vì getCount().
  4. Ghi đè createFragment() thay vì getItem() trong bộ chuyển đổi dựa trên mảnh khác.

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;
    }
}

Tái cấu trúc giao diện TabLayout

ViewPager2 đưa ra các thay đổi đối với tính năng tích hợp TabLayout. Nếu bạn hiện đang sử dụng ViewPager có đối tượng TabLayout để hiển thị theo chiều ngang các thẻ để điều hướng, bạn cần tái cấu trúc đối tượng TabLayout cho tích hợp với ViewPager2.

TabLayout đã được tách khỏi ViewPager2 và hiện có sẵn như một phần của Thành phần Material. Điều này có nghĩa là để sử dụng thẻ này, bạn cần thêm phần phụ thuộc thích hợp vào tệp build.gradle:

Groovy

implementation "com.google.android.material:material:1.1.0-beta01"

Kotlin

implementation("com.google.android.material:material:1.1.0-beta01")

Bạn cũng cần thay đổi vị trí của phần tử TabLayout trong hệ phân cấp của tệp bố cục XML của bạn. Với ViewPager, phần tử TabLayout được khai báo là phần tử phần tử con của phần tử ViewPager; nhưng với ViewPager2, phần tử TabLayout được khai báo ngay phía trên phần tử ViewPager2, ở cùng cấp:

<!-- 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>

Cuối cùng, bạn phải cập nhật mã đính kèm đối tượng TabLayout vào đối tượng Đối tượng ViewPager. Trong khi TabLayout sử dụng setupWithViewPager() riêng để tích hợp với ViewPager, bạn phải có TabLayoutMediator để tích hợp với ViewPager2.

Đối tượng TabLayoutMediator cũng xử lý tác vụ tạo tiêu đề trang cho đối tượng TabLayout, tức là lớp bộ chuyển đổi không cần ghi đè 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();
    }
    ...
}

Hỗ trợ các phần tử có thể cuộn lồng nhau

ViewPager2 vốn không hỗ trợ khung hiển thị cuộn lồng nhau trong trường hợp thành phần hiển thị cuộn có cùng hướng với đối tượng ViewPager2 chứa nó. Ví dụ: thao tác cuộn sẽ không hoạt động đối với chế độ xem cuộn dọc bên trong đối tượng ViewPager2 hướng dọc.

Để hỗ trợ khung hiển thị cuộn bên trong đối tượng ViewPager2 có cùng hướng, bạn phải gọi requestDisallowInterceptTouchEvent() trên đối tượng ViewPager2 khi bạn thay vào đó sẽ cuộn phần tử lồng nhau. Thanh cuộn lồng nhau ViewPager2 mẫu minh hoạ một cách để giải quyết vấn đề này thông qua một chiến lược linh hoạt bố cục trình bao bọc tuỳ chỉnh.

Tài nguyên khác

Để tìm hiểu thêm về ViewPager2, hãy xem các tài nguyên bổ sung sau đây.

Mẫu

Video