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
ViewPager
sử dụngPagerAdapter
để phân trang qua các chế độ xem, hãy sử dụngRecyclerView.Adapter
vớiViewPager2
. - Khi
ViewPager
sử dụngFragmentPagerAdapter
để chuyển qua một trang số lượng mảnh cố định, hãy sử dụngFragmentStateAdapter
vớiViewPager2
. - Khi
ViewPager
sử 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ụngFragmentStateAdapter
vớ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
FragmentActivity
hoặc đối tượngFragment
mà trong đó Đối tượngViewPager2
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ượ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)