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
ViewPager2 và Tạ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:
- Khi
ViewPagersử dụngPagerAdapterđể phân trang qua các chế độ xem, hãy sử dụngRecyclerView.AdaptervớiViewPager2. - Khi
ViewPagersử dụngFragmentPagerAdapterđể chuyển qua một trang số lượng mảnh cố định, hãy sử dụngFragmentStateAdaptervớiViewPager2. - Khi
ViewPagersử dụngFragmentStatePagerAdapterđể chuyển trang qua một số lượng lớn mảnh hoặc không xác định, hãy sử dụngFragmentStateAdaptervớiViewPager2.
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
FragmentActivityhoặc đối tượngFragmentmà trong đó Đối tượngViewPager2nằ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
FragmentManagervà một đối tượngLifecycle.
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ứccreateFragment()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:
- Thay đổi lớp cấp cao thành
RecyclerView.Adapterđể phân trang thông qua các khung hiển thị hoặcFragmentStateAdapterđể phân trang qua các phân đoạn. - 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.
- Ghi đè
getItemCount()thay vìgetCount(). - 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
- Mẫu ViewPager2 trên GitHub
Video
- Bước sang trang mới: Di chuyển sang ViewPager2 (Hội nghị Nhà phát triển Android 2019)