ViewPager adalah salah satu komponen Perpustakaan Dukungan Android yang paling terkenal dan banyak digunakan. Semua komidi putar, papan atas, dan penggeser paling sederhana dibuat di atasnya. Pada bulan Februari 2019, tim pengembangan AndroidX merilis ViewPager2. Mari kita lihat apa saja prasyarat-prasyarat ini dan apa kelebihan dari versi komponen yang diperbarui.

ViewPager 2
Pada saat menulis posting (Juli 2019), versi beta dari
ViewPager2 tersedia , yang berarti bahwa masalah yang disebutkan di bawah ini dapat diperbaiki dan fungsionalitas ditingkatkan dan diperluas. Pengembang berjanji di masa depan untuk menambahkan dukungan untuk TabLayout (sementara itu hanya dapat bekerja dengan versi pertama), mengoptimalkan kinerja adaptor, membuat banyak koreksi kecil dan menyelesaikan dokumentasi.
Integrasi
Komponen tidak disertakan dengan paket standar, tetapi terhubung secara terpisah. Untuk melakukan ini, tambahkan baris berikut ke blok dependensi di skrip gradle dari modul Anda:
implementation "androidx.viewpager2:viewpager2:1.0.0-beta02"
Implementasi
Mari kita mulai dengan kabar baik: beralih dari versi pertama ke versi kedua adalah sesederhana mungkin dan berujung pada perubahan impor. Sintaksis lama yang baik tidak tersentuh: metode
getCurrentItem () mengembalikan halaman saat ini,
ViewPager2.onPageChangeCallback memungkinkan
Anda untuk berlangganan ke
keadaan pager, adaptor masih diinstal melalui
setAdapter ().
Perlu menggali lebih dalam, karena menjadi jelas bahwa pager pertama dan kedua tidak memiliki kesamaan kecuali antarmuka. Keakraban dengan penerapan metode setAdapter () tidak menyisakan ruang untuk keraguan:
public final void setAdapter(@Nullable Adapter adapter) { mRecyclerView.setAdapter(adapter); }
Ya, ViewPager2 hanyalah pembungkus dari
RecyclerView . Di satu sisi, ini merupakan nilai tambah yang besar, di sisi lain - ini menambah sakit kepala. Menyamarkan
RecyclerView sebagai selebaran menjadi mungkin dengan munculnya
PagerSnapHelper . Kelas ini mengubah fisika gulir. Ketika pengguna melepaskan jarinya,
PagerSnapHelper menghitung item daftar mana yang paling dekat dengan garis tengah daftar, dan dengan animasi yang halus selaraskan tepat di tengah. Jadi, jika geseknya cukup tajam, daftar akan bergulir ke elemen berikutnya, jika tidak - dengan animasi kembali ke keadaan semula.
new PagerSnapHelper().attachToRecyclerView(mRecyclerView);

Saat menggunakan PagerSnapHelper, pastikan lebar dan tinggi RecyclerView itu sendiri, serta semua ViewHolders-nya, disetel ke MATCH_PARENT. Jika tidak, perilaku SnapHelper tidak dapat diprediksi, bug dapat terjadi di tempat yang benar-benar tidak terduga. Semua ini membuat korsel elemen dengan ketinggian kecil agak memakan waktu, meskipun mungkin.
Dengan semua hal di atas, dalam tata letak widget akan terlihat seperti ini:
<androidx.viewpager2.widget.ViewPager2 android:id="@+id/main_pager" android:layout_width="match_parent" android:layout_height="match_parent" />
Dalam paket yang sama dengan
ViewPager2, kita juga dapat menemukan kelas
ScrollEventAdapter , yang membantu menjaga kontinuitas sintaksis.
ScrollEventAdapter mengimplementasikan
RecyclerView.OnScrollListener dan mengubah acara gulir
menjadi peristiwa
OnPageChangeCallback .
@Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { if (mAdapterState != STATE_IN_PROGRESS_MANUAL_DRAG && newState == RecyclerView.SCROLL_STATE_DRAGGING) { ... dispatchStateChanged(SCROLL_STATE_DRAGGING); return; } ... }
Sekarang
OnPageChangeCallback diwakili bukan oleh antarmuka, tetapi oleh kelas abstrak, yang memungkinkan Anda untuk mendefinisikan kembali hanya metode yang diperlukan (dalam kebanyakan kasus, Anda hanya perlu
oPageSelected (Int) , yang berfungsi ketika halaman tertentu dipilih):
main_pager.registerOnPageChangeCallback( object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) {
Fitur
Yang perlu diperhatikan adalah metode setPageTransformer () , yang menggunakan
ViewPager2.PageTransformer sebagai parameter. Ini menetapkan
panggilan balik untuk setiap acara pemilihan halaman dan berfungsi untuk mengatur animasi sendiri untuk halaman ini.
Callback menerima tampilan halaman saat ini dan nomornya sebagai input. Analog terdekat dengan metode ini adalah
ItemAnimator dari
RecyclerView .
Dalam versi baru perpustakaan, dua implementasi transformator ditambahkan:
CompositePageTransformer dan
MarginPageTransformer . Yang pertama bertanggung jawab untuk menggabungkan transformer untuk menerapkan beberapa transformasi pada satu pager sekaligus, dan yang kedua untuk membuat indentasi antar halaman:

Selain itu, widget baru mendukung perubahan orientasi: hanya dengan memanggil metode
setOrientation () , Anda dapat mengubah pager menjadi daftar vertikal dengan gesek dari atas ke bawah:
main_pager.setOrientation(ViewPager2.ORIENTATION_VERTICAL)
Ini terjadi lagi berkat transisi ke
RecyclerView : di bawah tenda, perubahan dalam orientasi
LayoutManager disebut , yang bertanggung jawab untuk menampilkan item daftar. Perlu dicatat bahwa mendelegasikan sejumlah besar tugas ke kelas lain telah menguntungkan komponen baru: daftarnya telah menjadi jauh lebih kompak dan mudah dibaca.
Ini bukan akhir dari kesenangan. Dalam satu pembaruan,
ViewPager2 menerima dukungan untuk
ItemDecoration : kelas
pembantu untuk mendekorasi
View anak. Mekanisme ini dapat digunakan untuk menggambar pemisah antara elemen, batas, sorotan sel.
Sudah ada banyak implementasi dekorator siap pakai, karena selama bertahun-tahun mereka telah berhasil digunakan ketika bekerja dengan
RecyclerView yang biasa. Semua perkembangan sekarang berlaku untuk pager. Di luar kotak, implementasi standar pemisah pager tersedia:
main_pager.addItemDecoration( DividerItemDecoration(this, RecyclerView.HORIZONTAL) )
Bersamaan dengan pembaruan berikutnya pada Mei 2019,
ViewPager2 menambahkan metode penting lainnya:
setOffscreenPageLimit (Int) . Dia bertanggung jawab untuk berapa banyak elemen ke kanan dan kiri pusat akan diinisialisasi dalam pager. Meskipun
RecyclerView bertanggung jawab untuk menyimpan dan menampilkan
Tampilan secara default, menggunakan metode ini Anda dapat secara eksplisit mengatur jumlah item yang ingin dimuat.
Adaptor
Penerus ideologis adaptor pager pertama adalah
FragmentStateAdapter : antarmuka interaksi dan penamaan kelas hampir sama. Perubahan hanya memengaruhi penamaan beberapa metode. Jika sebelumnya diperlukan untuk mengimplementasikan fungsi abstrak
getItem (posisi) untuk mengembalikan instance
Fragment yang diinginkan untuk posisi yang diberikan, dan penamaan ini dapat diinterpretasikan dalam dua cara, sekarang fungsi ini telah diubah namanya menjadi
createFragment (posisi) . Jumlah total fragmen disediakan seperti sebelumnya oleh fungsi
getCount () .
Dari perubahan struktural penting pada antarmuka, perlu juga dicatat bahwa adaptor sekarang memiliki kemampuan untuk mengontrol siklus hidup unsur-unsurnya, oleh karena itu, bersama dengan
FragmentManager dalam konstruktor, ia menerima
objek Siklus Hidup , baik
Kegiatan atau
Fragmen . Karena itu, untuk keamanan, metode
saveState () dan
restoreState () dinyatakan final dan ditutup untuk warisan.
Kelas
FragmentViewHolder bertanggung jawab untuk menyimpan fragmen di dalam
RecyclerView . Metode
onCreateViewHolder () dari
FragmentStateAdapter memanggil
FragmentViewHolder.create () .
static FragmentViewHolder create(ViewGroup parent) { FrameLayout container = new FrameLayout(parent.getContext()); container.setLayoutParams( new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) ); container.setId(ViewCompat.generateViewId()); container.setSaveEnabled(false); return new FragmentViewHolder(container); }
Ketika metode
onBindViewHolder () dipanggil , pengidentifikasi elemen pada posisi saat ini dan pengidentifikasi
ViewHolder terkait , untuk lebih lanjut melampirkan fragmen ke dalamnya:
final long itemId = holder.getItemId(); final int viewHolderId = holder.getContainer().getId(); final Long boundItemId = itemForViewHolder(viewHolderId); ... mItemIdToViewHolder.put(itemId, viewHolderId); ensureFragment(position);
Dan akhirnya, ketika melampirkan wadah dari
ViewHolder ke hierarki tampilan, sebuah
FragmentTransaction dieksekusi, menambahkan
Fragmen ke wadah:
void placeFragmentInViewHolder(@NonNull final FragmentViewHolder holder) { Fragment fragment = mFragments.get(holder.getItemId()); ... scheduleViewAttach(fragment, container); mFragmentManager.beginTransaction() .add(fragment, "f" + holder.getItemId()) .setMaxLifecycle(fragment, STARTED) .commitNow(); ... }
Dengan demikian, dua penggunaan
ViewPager2 muncul : melalui mewarisi kelas adaptor, baik langsung dari
RecyclerView.Adapter atau dari
FragmentStateAdapter .
Tentunya Anda akan memiliki pertanyaan: mengapa menggunakan pager kedua dengan Fragmen dan adaptor untuk mereka ketika ada versi pertama yang berfungsi normal?
ViewPager jauh dari "peluru perak" ketika bekerja dengan daftar data dinamis yang besar. Ini bagus untuk membuat komidi putar dengan set gambar atau spanduk statis, tetapi feed berita dengan paginasi memuat postingan iklan, menyaring melahirkan monster yang didukung keras dan jelek. Cepat atau lambat, Anda pasti akan menemukan hasrat yang membara untuk menulis ulang segala sesuatu di
RecyclerView . Sekarang Anda tidak perlu melakukan ini, karena pager itu sendiri mengubahnya, meminjam kemampuannya yang kuat untuk bekerja dengan daftar dinamis, sambil membungkusnya dalam sintaksis yang biasa.
Satu-satunya hal yang dapat ditawarkan
PagerAdapter kepada
kami adalah metode
notifyDataSetChanged () , yang memaksa
ViewPager untuk menggambar ulang semua item daftar yang diberikan. Anda mungkin memperhatikan bahwa tidak ada yang menghentikan kami dari menyimpan daftar posisi untuk elemen yang ada dan mengembalikan
POSITION_UNCHANGED dari metode
getItemPosition () untuk mereka, hanya itu. Namun, solusi ini tidak dapat disebut cantik, itu agak rumit, apalagi, sulit untuk memperluas kasus-kasus ketika elemen-elemen dalam daftar terus berubah, dan tidak hanya ditambahkan secara berurutan sampai akhir.
FragmentStateAdapter memiliki gudang penuh metode
RecyclerView.Adapter , sehingga logika redrawing elemen dapat dikonfigurasi jauh lebih fleksibel. Selain itu, bersama dengan
FragmentStateAdapter, Anda dapat menggunakan
DiffUtil , yang memungkinkan Anda untuk secara otomatis mengotomatiskan pekerjaan pemberitahuan perubahan.

Perhatian! Untuk metode notify ... agar berfungsi dengan benar (kecuali untuk notifyDataSetChanged ), metode getItemId (Int) dan c ontainsItem (Long) harus didefinisikan ulang. Ini dilakukan karena implementasi default hanya melihat pada nomor halaman, dan jika, misalnya, Anda menambahkan elemen baru setelah yang sekarang, itu tidak akan ditambahkan, karena getItemId akan tetap tidak berubah. Contoh meng-override dua metode ini berdasarkan daftar elemen bertipe Int :
override fun getItemId(position: Int): Long { return items[position].toLong() } override fun containsItem(itemId: Long): Boolean { return items.contains(itemId.toInt()) }
Alasan utama kemunculan
ViewPager2 adalah keengganan untuk menemukan kembali roda. Di satu sisi,
tim pengembangan
AndroidX jelas siap untuk meninggalkan
ViewPager yang sudah usang sekarang dan tentu saja tidak akan berinvestasi dalam memperluas fungsionalitasnya. Ya, dan mengapa? Lagi pula,
RecyclerView sudah mengetahui semua yang diperlukan. Di sisi lain, penghapusan dan penghentian dukungan untuk komponen yang banyak digunakan jelas tidak akan menambah loyalitas masyarakat.
Untuk meringkas:
ViewPager2 jelas layak diperhatikan, meskipun saat ini bukan tanpa kelemahan serius.
Cons
- Kelembaban dan sejumlah besar bug (dapat dimaafkan untuk versi beta);
- Kedekatan. RecyclerView adalah bidang pribadi dari ViewPager2 , yang menghalangi kita dari banyak peluang: tidak mungkin menerapkan swipe-to-dismiss atau drag-n-drop ( ItemTouchHelper terhubung langsung ke RecyclerView ), Anda tidak dapat mendefinisikan kembali ItemAnimator dengan cara apa pun, tidak mengakses LayoutManager secara langsung dan menggunakan RecycledViewPool secara langsung. Namun, dengan rilis versi baru komponen, jumlah metode antarmuka yang diwarisi dari RecyclerView semakin bertambah (misalnya, ItemDecoration ), dan kami berharap dapat menambahkan metode yang hilang di masa mendatang.
Pro
- Mendukung semua keuntungan RecyclerView.Adapter : menggabungkan elemen dari tipe yang berbeda dalam satu daftar, menambah dan menghapus elemen secara langsung selama swipe, animasi menggambar ulang isi daftar ketika mengubah;
- Dukungan untuk spektrum penuh metode notify ... dan perhitungan otomatis perubahan menggunakan DiffUtil ;
- Kemudahan transisi karena kesinambungan sintaksis;
- Dukungan untuk orientasi vertikal dan horizontal "out of the box";
- Dukungan RTL ;
- Mendukung ItemDecorator ;
- Dukungan untuk menggulir perangkat lunak melalui fakeScrollBy () ;
- Kemampuan untuk secara manual mengatur jumlah item yang dimuat;
- Kemampuan untuk menggunakan salah satu solusi open-source yang siap pakai untuk mengurangi kode boilerplate , yang tidak dapat dihindari ketika menulis RecyclerView.Adapter kustom. Misalnya, EasyAdapter .
Sebagai ringkasan, saya ingin mengatakan bahwa
ViewPager2 benar-benar layak untuk dilihat
lebih dekat . Ini adalah solusi yang menjanjikan, dapat dikembangkan, dan fungsional. Dan meskipun masih terlalu dini untuk meluncurkan
widget baru dalam produksi, aman untuk mengatakan bahwa setelah rilis penuh ia dapat dan harus sepenuhnya menggantikan leluhurnya.
Bagi mereka yang berani dan tegas, yang terinspirasi artikel untuk bereksperimen,
PagerSnapHelper muncul di versi 28 dari
Perpustakaan Dukungan , yang berarti Anda dapat menggunakannya bersama dengan
RecyclerView Anda dengan membuat
ViewPager2 sendiri .
Contoh operasi dari
ViewPager2 dan
FragmentStateAdapter .
Rilis-catatan resmi ViewPager2