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ề cách di 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
, hãy đọc nội dung Trượt giữa các mảnh bằng ViewPager2 và Tạo thành phần hiển thị vuốt có thẻ bằng ViewPager2 để biết thêm thông tin.
Lợi ích của việc di chuyển sang ViewPager2
Lý do chính để di chuyển là ViewPager2
đang được hỗ trợ phát triển còn ViewPager
thì không. Tuy nhiên, ViewPager2
cũng cung cấp một số ưu điểm cụ thể khác.
Hỗ trợ hướng dọc
ViewPager2
hỗ trợ chế độ phân trang theo chiều dọc ngoài chế độ phân trang theo chiều ngang truyền thống. Bạn có thể bật tính năng phân trang theo chiều dọc cho một phần tử ViewPager2
bằng cách đặt thuộc tính android:orientation
của phần tử đó:
<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 phương thức setOrientation().
Hỗ trợ từ phải sang trái
ViewPager2
hỗ trợ phân trang từ phải sang trái (RTL). Tính năng phân trang RTL được tự động bật khi thích hợp dựa trên ngôn ngữ, nhưng bạn cũng có thể bật tính năng phân trang RTL theo cách thủ công cho phần tử ViewPager2
bằng cách đặt thuộc tính android:layoutDirection
của phần tử đó:
<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 phương thức 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, 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.
Tức là ứng dụng của bạn có thể tự động sửa đổi bộ sưu tập mảnh trong thời gian chạy và ViewPager2
sẽ hiển thị chính xác bộ sưu tập đã sửa đổi.
DiffUtil
ViewPager2
được xây dựng trên RecyclerView
, tức là có quyền truy cập vào lớp 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
vốn có thể tận dụng ảnh động thay đổi tập dữ liệu của lớp RecyclerView
.
Di chuyển ứng dụng của bạn sang ViewPager2
Hãy làm theo các bước sau để cập nhật đối tượng ViewPager
trong ứng dụng của bạn 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 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 trang mới cho đối tượng. Tuỳ thuộc vào trường hợp sử dụng, ViewPager
sử dụng ba lớp trừu tượng khác nhau. ViewPager2
chỉ sử dụng hai 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
,
hãy 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
ViewPager
dùngPagerAdapter
để phân trang qua các khung hiển thị, hãy sử dụngRecyclerView.Adapter
vớiViewPager2
. - Khi
ViewPager
sử dụngFragmentPagerAdapter
để phân trang qua một số lượng nhỏ mảnh cố định, hãy sử dụngFragmentStateAdapter
vớiViewPager2
. - Khi
ViewPager
sử dụngFragmentStatePagerAdapter
để phân trang số lượng mảnh lớn hoặc không xác định, hãy sử dụngFragmentStateAdapter
cùng vớiViewPager2
.
Tham số hàm khởi tạo
Các lớp 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 mở rộng FragmentStateAdapter
cho một lớp bộ chuyển đổi ViewPager2
, bạn sẽ có các lựa chọn sau cho tham số hàm khởi tạo:
- Đối tượng
FragmentActivity
hoặc đối tượngFragment
là nơi chứa đối tượngViewPager2
. Trong hầu hết các trường hợp, đây là lựa chọn phù hợp hơn. - Đối tượng
FragmentManager
và đối tượngLifecycle
.
Các lớp 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 ViewPager
:
- Thay vì
getCount()
, hãy ghi đègetItemCount()
. Ngoài tên, phương thức này không thay đổi. - Thay vì
getItem()
, hãy ghi đècreateFragment()
trong các lớp bộ chuyển đổi dựa trên mảnh. Hãy đảm bảo phương thứccreateFragment()
mới của bạn luôn cung cấp một thực thể mới của mảnh mỗi khi hàm được gọi thay vì sử dụng lại các thực thể.
Tóm tắt
Tóm lại, để chuyển đổi một lớp bộ chuyển đổi ViewPager
để sử dụng với ViewPager2
,
bạn phải thực hiện những thay đổi sau:
- Thay đổi lớp cấp cao thành
RecyclerView.Adapter
để phân trang thông qua các thành phần hiển thị hoặcFragmentStateAdapter
để phân trang thông qua các mảnh. - Thay đổi các 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 các lớp bộ chuyển đổi dựa trên mảnh.
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
giới thiệu các thay đổi đối với tính năng tích hợp TabLayout
. Nếu đang sử dụng ViewPager
với đối tượng TabLayout
nhằm hiển thị các thẻ theo chiều ngang cho việc điều hướng, bạn cần tái cấu trúc đối tượng TabLayout
để 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 phần tử 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. Với ViewPager
, phần tử TabLayout
được khai báo là 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 ViewPager
. Mặc dù TabLayout
dùng phương thức setupWithViewPager()
riêng để tích hợp với ViewPager
, nhưng bạn phải có một thực thể 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
, nghĩa 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ử lồng nhau có thể cuộn
ViewPager2
không hỗ trợ sẵn các thành phần 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 thành phần hiển thị đó. Ví dụ: thao tác cuộn sẽ không hoạt động đối với khung hiển thị cuộn dọc bên trong đối tượng ViewPager2
theo 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 muốn cuộn phần tử lồng nhau. Mẫu cuộn lồng ViewPager2 minh hoạ một cách giải quyết vấn đề này bằng bố cục trình bao bọc tuỳ chỉnh linh hoạt.
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)