Tidak ada kisah yang lebih menyedihkan di dunia
dari kisah ViewPager'e dan SET'e
Saya ingin memperingatkan bahwa pembuatnya adalah android pemula, jadi artikelnya mengandung begitu banyak ketidakakuratan teknis sehingga Anda sebaiknya diperingatkan bahwa pernyataan yang secara teknis andal dapat muncul dalam artikel tersebut.
Di mana backend mengarah
Sepanjang hidupku aku melihat backend. Awal tahun 2019, sudah ada satu proyek yang sangat ambisius, tetapi belum selesai. Perjalanan sia-sia ke Zurich untuk wawancara dengan satu perusahaan pencarian. Musim dingin, tanah, tidak ada mood. Tidak ada kekuatan dan keinginan untuk menarik proyek lebih jauh.
Saya ingin melupakan backend mengerikan ini selamanya. Untungnya, takdir memberi saya ide - itu adalah aplikasi mobile. Fitur utamanya adalah penggunaan kamera yang tidak standar. Pekerjaan mulai mendidih. Butuh sedikit waktu, dan sekarang prototipe sudah siap. Rilis proyek sudah dekat dan semuanya baik-baik saja dan proporsional, sampai saya memutuskan untuk membuat pengguna " nyaman ".
Tidak ada yang ingin mengklik tombol menu kecil pada tahun 2019, baik kanan dan kiri mereka ingin menggesek layar. Dikatakan - dilakukan, dilakukan - rusak. Jadi ViewPager
pertama kali muncul di proyek saya (saya telah menguraikan beberapa istilah untuk backend yang sama dengan saya - hanya memindahkan kursor). Dan Transisi Elemen Bersama (selanjutnya SET atau transisi) - elemen tanda tangan Desain Material , dengan tegas menolak untuk bekerja dengan ViewPager
, membuat saya memiliki pilihan: gesekan, atau animasi transisi yang indah di antara layar. Saya tidak ingin menolak yang satu atau yang lain. Jadi pencarian saya dimulai.
Jam belajar: puluhan topik di forum dan pertanyaan yang belum terjawab di StackOverflow. Tidak peduli apa yang saya buka, saya ditawari untuk melakukan transisi dari RecyclerView ke ViewPager atau "melampirkan pisang raja Fragment.postponeEnterTransition()
".
Obat tradisional tidak membantu, dan saya memutuskan untuk mendamaikan ViewPager
dan Shared Element Transition
sendiri.
Saya mulai berpikir: "Masalahnya muncul pada saat pengguna berpindah dari satu halaman ke halaman lain ...". Dan kemudian saya sadar: "Anda tidak akan memiliki masalah dengan SET selama perubahan halaman, jika Anda tidak mengubah halaman".
Kita dapat melakukan transisi pada halaman yang sama, dan kemudian hanya mengganti halaman saat ini dengan halaman target di ViewPager
.
Pertama, buat fragmen yang dengannya kita akan bekerja.
SmallPictureFragment small_picture_fragment = new SmallPictureFragment(); BigPictureFragment big_picture_fragment = new BigPictureFragment();
Mari kita coba ubah fragmen di halaman saat ini ke yang lain.
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Kami meluncurkan aplikasi dan ... dengan lancar beralih ke layar kosong. Apa alasannya
Ternyata wadah dari setiap halaman adalah ViewPager
itu sendiri, tanpa perantara seperti Page1Container
, Page2Container
. Oleh karena itu, hanya mengubah satu halaman ke halaman lainnya tidak berfungsi, seluruh halaman akan diganti.
Nah, untuk mengubah konten setiap halaman secara terpisah, kami membuat beberapa fragmen wadah untuk setiap halaman.
RootSmallPictureFragment root_small_pic_fragment = new RootSmallPictureFragment(); RootBigPictureFragment root_big_pic_fragment = new RootBigPictureFragment();
Sesuatu tidak akan mulai lagi.
java.lang.IllegalStateException: Tidak dapat mengubah ID kontainer fragmen BigPictureFragment {...}: tadinya 2131165289 sekarang 2131165290
Kami tidak dapat melampirkan fragmen halaman kedua ( BigPictureFragment
) ke yang pertama, karena sudah terlampir pada wadah halaman kedua.
Setelah mengepalkan gigi, kami menambahkan lebih banyak fragmen ganda.
SmallPictureFragment small_picture_fragment_fake = new SmallPictureFragment(); BigPictureFragment big_picture_fragment_fake = new BigPictureFragment();
Menghasilkan! Kode transisi yang pernah saya salin dari bentangan GitHub sudah berisi animasi fade in dan fade out . Oleh karena itu, sebelum transisi, semua elemen statis dari fragmen pertama menghilang, lalu gambar bergerak, dan baru kemudian elemen-elemen halaman kedua muncul. Bagi pengguna, ini terlihat seperti perpindahan nyata antar halaman.
Semua animasi telah berlalu, tetapi ada satu masalah. Pengguna masih di halaman pertama, tetapi harus di halaman kedua.
Untuk memperbaikinya, kami dengan hati-hati mengganti halaman ViewPager
terlihat ViewPager
yang kedua. Dan kemudian kita mengembalikan konten halaman pertama ke keadaan awal.
handler.postDelayed( () -> {
Apa hasilnya? (animasi - 2,7 mb) Seluruh kode sumber | public class FragmentTransitionUtil { |
| |
| private static final long FADE_DEFAULT_TIME = 500; |
| private static final long MOVE_DEFAULT_TIME = 1000; |
| |
| public static void perform( |
| MainActivity activity, |
| Fragment previousFragment, |
| Fragment nextFragment, |
| Map<View, String> sharedElements, |
| int nextPage |
| |
| ) { |
| FragmentManager fragmentManager = activity.getSupportFragmentManager(); |
| FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); |
| |
| if (previousFragment != null) { |
| |
| // 1. Exit for Previous Fragment |
| Fade exitFade = new Fade(); |
| exitFade.setDuration(FADE_DEFAULT_TIME); |
| previousFragment.setExitTransition(exitFade); |
| |
| // 2. Shared Elements Transition |
| TransitionSet enterTransitionSet = new TransitionSet(); |
| enterTransitionSet.addTransition( |
| new TransitionSet() { |
| |
| { |
| setOrdering(ORDERING_TOGETHER); |
| addTransition(new ChangeBounds()). |
| addTransition(new ChangeTransform()). |
| addTransition(new ChangeImageTransform()); |
| } |
| } |
| ); |
| |
| enterTransitionSet.setDuration(MOVE_DEFAULT_TIME); |
| enterTransitionSet.setStartDelay(FADE_DEFAULT_TIME); |
| nextFragment.setSharedElementEnterTransition(enterTransitionSet); |
| |
| // 3. Enter Transition for New Fragment |
| Fade enterFade = new Fade(); |
| enterFade.setStartDelay(MOVE_DEFAULT_TIME + FADE_DEFAULT_TIME); |
| enterFade.setDuration(FADE_DEFAULT_TIME); |
| nextFragment.setEnterTransition(enterFade); |
| |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
| if (sharedElements != null) { |
| for (Map.Entry<View, String> viewStringEntry : sharedElements.entrySet()) { |
| View view = viewStringEntry.getKey(); |
| String transName = viewStringEntry.getValue(); |
| view.setTransitionName(transName); |
| |
| fragmentTransaction.addSharedElement( |
| view, |
| transName |
| ); |
| } |
| } |
| } |
| |
| int fragmentContId = ((ViewGroup) previousFragment.getView().getParent()).getId(); |
| fragmentTransaction.replace(fragmentContId, nextFragment); |
| fragmentTransaction.commit(); |
| |
| |
| final Handler handler = new Handler(); |
| handler.postDelayed( |
| () -> { |
| // Stealthy changing page visible to user. He wonβt notice! |
| activity.viewPager.setCurrentItem(nextPage, false); |
| FragmentTransaction transaction = fragmentManager.beginTransaction(); |
| // Restore previous fragment. It contains inappropriate view now |
| transaction.replace(fragmentContId, previousFragment); |
| transaction.commitAllowingStateLoss(); |
| }, |
| FADE_DEFAULT_TIME + MOVE_DEFAULT_TIME + FADE_DEFAULT_TIME |
| ); |
| |
| |
| } |
| } |
| |
| } |
Proyek ini dapat dilihat di GitHub .
Untuk meringkas. Kode mulai terlihat jauh lebih solid: daripada 2 fragmen awal, saya mendapat sebanyak 6, instruksi muncul di dalamnya yang mengontrol kinerja, mengganti fragmen pada waktu yang tepat. Dan ini hanya di demo.
Dalam proyek yang sama, satu demi satu dalam kode, cadangan mulai muncul di tempat-tempat yang paling tak terduga. Mereka tidak membiarkan aplikasi berantakan ketika pengguna mengklik tombol dari halaman "salah" atau membatasi kerja latar belakang dari duplikat fragmen.
Ternyata android tidak memiliki panggilan balik untuk menyelesaikan transisi , dan waktu pelaksanaannya sangat sewenang-wenang dan tergantung pada banyak faktor (misalnya, seberapa cepat RecyclerView
memuat dalam fragmen yang dihasilkan). Hal ini menyebabkan fakta bahwa penggantian fragmen di handler.postDelayed()
sering dieksekusi terlalu cepat atau terlambat, yang hanya memperburuk masalah sebelumnya.
Sorotan terakhir adalah bahwa selama animasi, pengguna cukup menggesek ke halaman lain dan menonton dua layar kembar, setelah itu aplikasi juga menariknya ke layar yang diinginkan.
Artefak yang menarik dari pendekatan ini (animasi - 2,7 mb) Keadaan ini tidak cocok untukku, dan aku, penuh kemarahan yang saleh, mulai mencari solusi lain.
Mencoba PageTransformer
Masih belum ada jawaban di Internet, dan saya berpikir: bagaimana lagi transisi ini dapat dihidupkan. Sesuatu dalam subkorteks kesadaran berbisik kepada saya: "Gunakan PageTransformer
, Luke." Gagasan itu tampak menjanjikan bagi saya dan saya memutuskan untuk mendengarkan.
Idenya adalah membuat PageTransformer
, yang, tidak seperti Android SET , tidak akan membutuhkan pengulangan beberapa setTransitionName(transitionName)
dan FragmentTransaction.addSharedElement(sharedElement,name)
di kedua sisi transisi. Ini akan memindahkan elemen setelah babatan dan memiliki antarmuka yang sederhana seperti:
public void addSharedTransition(int fromViewId, int toViewId)
Kami melanjutkan ke pengembangan. Saya akan menyimpan data dari metode addSharedTransition(fromId, toId)
ke Set
from Pair
dan mendapatkannya dalam metode PageTransfomer
public void transformPage(@NonNull View page, float position)
Di dalam, saya akan melihat semua pasangan View
tersimpan, di antaranya saya perlu membuat animasi. Dan saya akan mencoba memfilternya sehingga hanya elemen yang terlihat yang dianimasikan.
Pertama, mari kita periksa apakah elemen yang perlu dianimasikan telah dibuat. Kami bukan pemilih, dan jika View
tidak dibuat sebelum dimulainya animasi, kami tidak akan memecah seluruh animasi (seperti Transisi Elemen Bersama ), tetapi mengambilnya ketika elemen dibuat.
for (Pair<Integer,Integer> idPair : sharedElementIds) { Integer fromViewId = idPair.first; Integer toViewId = idPair.second; View fromView = activity.findViewById(fromViewId); View toView = activity.findViewById(toViewId); if (fromView != null && toView != null) {
Saya menemukan halaman-halaman di mana gerakan itu terjadi (bagaimana saya menentukan nomor halaman akan dijelaskan di bawah).
View fromPage = pages.get(fromPageNumber); View toPage = pages.get(toPageNumber);
Jika kedua halaman sudah dibuat, maka saya sedang mencari sepasang View
pada mereka, yang perlu saya menghidupkan.
If (fromPage != null && toPage != null) { fromView = fromPage.findViewById(fromViewId); toView = toPage.findViewById(toViewId);
Pada tahap ini, kami memilih View, yang terletak pada halaman di mana pengguna menggulir.
Saatnya untuk mendapatkan banyak variabel. Saya menghitung poin referensi:
Dalam cuplikan terakhir, saya mengatur slideToTheRight
, dan sudah dalam hal ini akan berguna bagi saya. Terjemahan masuk tergantung padanya, yang menentukan View
akan terbang ke tempatnya atau di suatu tempat di luar layar.
float pageWidth = getScreenWidth(); float sign = slideToTheRight ? 1 : -1; float translationY = (deltaY + deltaHeight / 2) * sign * (-position); float translationX = (deltaX + sign * pageWidth + deltaWidth / 2) * sign * (-position);
Menariknya, rumus offset untuk X
dan Y
untuk kedua View
, pada halaman awal dan halaman yang dihasilkan, ternyata sama, meskipun ada beberapa offset awal.
Tetapi dengan skalanya, sayangnya, trik ini tidak akan berhasil - Anda perlu mempertimbangkan apakah View
ini View
awal atau akhir dari animasi.
Ini mungkin mengejutkan bagi seseorang, tetapi transformPage(@NonNull View page, float position)
dipanggil berkali-kali: untuk setiap halaman yang di-cache (ukuran cache dapat disesuaikan). Dan, agar tidak menggambar ulang tampilan animasi beberapa kali, untuk setiap panggilan ke transformPage()
, kami hanya mengubah yang ada di page
saat ini.
Kami mengatur posisi dan skala elemen animasi Pilih halaman untuk menggambar animasi
ViewPager
tidak terburu-buru untuk berbagi informasi antara halaman mana yang sedang bergulir. Seperti yang saya janjikan, sekarang saya akan memberi tahu Anda bagaimana kami mendapatkan informasi ini. Di PageTransformer
kami PageTransformer
kami mengimplementasikan antarmuka ViewPager.OnPageChangeListener
lain. Setelah mempelajari output onPageScrolled()
melalui System.out.println()
saya sampai pada rumus berikut:
public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels ) { Set<Integer> visiblePages = new HashSet<>(); visiblePages.add(position); visiblePages.add(positionOffset >= 0 ? position + 1 : position - 1); visiblePages.remove(fromPageNumber); toPageNumber = visiblePages.iterator().next(); if (pages == null || toPageNumber >= pages.size()) toPageNumber = null; } public void onPageSelected(int position) { this.position = position; } public void onPageScrollStateChanged(int state) { if (state == SCROLL_STATE_IDLE) {
Itu saja. Kami berhasil! Animasi memonitor gerakan pengguna. Mengapa memilih antara gesek dan Transisi Elemen Bersama , saat Anda dapat meninggalkan semuanya.
Saat menulis artikel ini, saya menambahkan efek dari hilangnya elemen statis - itu masih sangat kasar, sehingga belum ditambahkan ke perpustakaan.
Lihat apa yang terjadi pada akhirnya (animasi - 2,4 mb) Seluruh kode sumber | /** |
| * PageTransformer that allows you to do shared element transitions between pages in ViewPager. |
| * It requires view pager sides match screen sides to function properly. I.e. ViewPager page width |
| * must be equal to screen width. <br/> |
| * Usage:<br/> |
| * <code> |
| * sharedElementPageTransformer.addSharedTransition(R.id.FirstPageTextView, R.id.SecondPageTextView)</code> |
| * </code> |
| * |
| * |
| */ |
| public class SharedElementPageTransformer implements ViewPager.PageTransformer, ViewPager.OnPageChangeListener { |
| /** Android need the correction while view scaling for some reason*/ |
| private static float MAGICAL_ANDROID_RENDERING_SCALE = 1; |
| // private static float MAGICAL_ANDROID_RENDERING_SCALE = 0.995f; |
| |
| // External variables |
| |
| private final Activity activity; |
| List<Fragment> fragments; |
| private Set<Pair<Integer,Integer>> sharedElementIds = new HashSet<>(); |
| |
| |
| |
| //Internal variables |
| |
| private List<View> pages; |
| private Map<View, Integer> pageToNumber = new HashMap<>(); |
| |
| private Integer fromPageNumber = 0; |
| private Integer toPageNumber; |
| |
| /** current view pager position */ |
| private int position; |
| |
| /** |
| * @param activity activity that hosts view pager |
| * @param fragments fragment that are in view pager in the SAME ORDER |
| */ |
| public SharedElementPageTransformer(Activity activity, List<Fragment> fragments) { |
| this.activity = activity; |
| this.fragments = fragments; |
| } |
| |
| |
| @Override |
| public void transformPage(@NonNull View page, float position) { |
| updatePageCache(); |
| if (fromPageNumber == null || toPageNumber == null) return; |
| |
| for (Pair<Integer,Integer> idPair : sharedElementIds) { |
| Integer fromViewId = idPair.first; |
| Integer toViewId = idPair.second; |
| |
| View fromView = activity.findViewById(fromViewId); |
| View toView = activity.findViewById(toViewId); |
| |
| if (fromView != null && toView != null) { |
| //Looking if current Shared element transition matches visible pages |
| |
| View fromPage = pages.get(fromPageNumber); |
| View toPage = pages.get(toPageNumber); |
| |
| if (fromPage != null && toPage != null) { |
| fromView = fromPage.findViewById(fromViewId); |
| toView = toPage.findViewById(toViewId); |
| |
| |
| // if both views are on pages user drag between apply transformation |
| if ( |
| fromView != null |
| && toView != null |
| ) { |
| // saving shared element position on the screen |
| float fromX = fromView.getX() - fromView.getTranslationX(); |
| float fromY = fromView.getY() - fromView.getTranslationY(); |
| float toX = toView.getX() - toView.getTranslationX(); |
| float toY = toView.getY() - toView.getTranslationY(); |
| float deltaX = toX - fromX; |
| float deltaY = toY - fromY; |
| |
| // scaling |
| float fromWidth = fromView.getWidth(); |
| float fromHeight = fromView.getHeight(); |
| float toWidth = toView.getWidth(); |
| float toHeight = toView.getHeight(); |
| float deltaWidth = toWidth - fromWidth; |
| float deltaHeight = toHeight - fromHeight; |
| |
| |
| int fromId = fromView.getId(); |
| int toId = toView.getId(); |
| |
| boolean slideToTheRight = toPageNumber > fromPageNumber; |
| |
| if (position <= -1) { |
| |
| } else if (position < 1) { |
| |
| float pageWidth = getSceenWidth(); |
| float sign = slideToTheRight ? 1 : -1; |
| |
| float translationY = (deltaY + deltaHeight / 2) * sign * (-position); |
| float translationX = (deltaX + sign * pageWidth + deltaWidth / 2) * sign * (-position); |
| |
| if (page.findViewById(fromId) != null) { |
| fromView.setTranslationX(translationX); |
| fromView.setTranslationY(translationY); |
| |
| float scaleX = (fromWidth == 0) ? 1 : (fromWidth + deltaWidth * sign * (-position)) / fromWidth; |
| float scaleY = (fromHeight == 0) ? 1 : (fromHeight + deltaHeight * sign * (-position)) / fromHeight; |
| |
| fromView.setScaleX(scaleX); |
| fromView.setScaleY(scaleY * MAGICAL_ANDROID_RENDERING_SCALE); |
| } |
| if (page.findViewById(toId) != null) { |
| |
| toView.setTranslationX(translationX); |
| toView.setTranslationY(translationY); |
| float scaleX = (toWidth == 0) ? 1 : (toWidth + deltaWidth * sign * (-position)) / toWidth; |
| float scaleY = (toHeight == 0) ? 1 :(toHeight + deltaHeight * sign * (-position)) / toHeight; |
| |
| toView.setScaleX(scaleX); |
| toView.setScaleY(scaleY); |
| } |
| |
| |
| } else { |
| } |
| |
| } |
| } |
| } |
| } |
| } |
| |
| private float getSceenWidth() { |
| Point outSize = new Point(); |
| activity.getWindowManager().getDefaultDisplay().getSize(outSize); |
| return outSize.x; |
| } |
| |
| /** |
| * Creating page cache array to determine if shared element on |
| * currently visible page |
| */ |
| private void updatePageCache() { |
| pages = new ArrayList<>(); |
| |
| for (int i = 0; i < fragments.size(); i++) { |
| View pageView = fragments.get(i).getView(); |
| pages.add(pageView); |
| pageToNumber.put(pageView, i); |
| |
| } |
| } |
| |
| |
| @Override |
| public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { |
| Set<Integer> visiblePages = new HashSet<>(); |
| |
| visiblePages.add(position); |
| visiblePages.add(positionOffset >= 0 ? position + 1 : position - 1); |
| visiblePages.remove(fromPageNumber); |
| |
| toPageNumber = visiblePages.iterator().next(); |
| |
| if (pages == null || toPageNumber >= pages.size()) toPageNumber = null; |
| } |
| |
| |
| @Override |
| public void onPageSelected(int position) { |
| this.position = position; |
| } |
| |
| @Override |
| public void onPageScrollStateChanged(int state) { |
| if (state == SCROLL_STATE_IDLE) { |
| fromPageNumber = position; |
| resetViewPositions(); |
| } |
| |
| } |
| |
| private void resetViewPositions() { |
| for (Pair<Integer, Integer> idPair : sharedElementIds) { |
| View sharedElement = activity.findViewById(idPair.first); |
| if(sharedElement != null) { |
| sharedElement.setTranslationX(0); |
| sharedElement.setTranslationY(0); |
| sharedElement.setScaleX(1); |
| sharedElement.setScaleY(1); |
| } |
| sharedElement = activity.findViewById(idPair.second); |
| if(sharedElement != null) { |
| |
| sharedElement.setTranslationX(0); |
| sharedElement.setTranslationY(0); |
| sharedElement.setScaleX(1); |
| sharedElement.setScaleY(1); |
| } |
| } |
| |
| } |
| |
| /** |
| * Set up shared element transition from element with <code>fromViewId</code> to |
| * element with <code>toViewId</code>. Note that you can setup each transition |
| * direction separately. e.g. <br/> |
| * <code>addSharedTransition(R.id.FirstPageTextView, R.id.SecondPageTextView)</code><br/> |
| * and<br/> |
| * <code>addSharedTransition(R.id.SecondPageTextView, R.id.FirstPageTextView)</code><br/> |
| * are different. |
| * @param fromViewId |
| * @param toViewId |
| */ |
| public void addSharedTransition(int fromViewId, int toViewId) { |
| addSharedTransition(fromViewId, toViewId, false); |
| } |
| |
| /** |
| * Set up shared element transition from element with <code>fromViewId</code> to |
| * element with <code>toViewId</code>. Note that you can setup each transition |
| * direction separately. e.g. <br/> |
| * <code>addSharedTransition(R.id.FirstPageTextView, R.id.SecondPageTextView)</code><br/> |
| * and<br/> |
| * <code>addSharedTransition(R.id.SecondPageTextView, R.id.FirstPageTextView)</code><br/> |
| * are different. |
| * @param fromViewId |
| * @param toViewId |
| * @param bothDirections to include backward transition from toViewId to fromViewId aswell |
| */ |
| public void addSharedTransition(int fromViewId, int toViewId, boolean bothDirections) { |
| sharedElementIds.add(new Pair<>(fromViewId, toViewId)); |
| if(bothDirections) { |
| sharedElementIds.add(new Pair<>(toViewId, fromViewId)); |
| } |
| } |
| /** |
| * In case there is "ladder" appears between while transition. |
| * You may try to tune that magical scale to get rid of it. |
| * @param magicalAndroidRenderingScale float between 0 and infinity. Typically very close to 1.0 |
| */ |
| public static void setMagicalAndroidRenderingScale(float magicalAndroidRenderingScale) { |
| MAGICAL_ANDROID_RENDERING_SCALE = magicalAndroidRenderingScale; |
| } |
| } |
Seperti apa fungsinya dengan perpustakaan
Konfigurasi ini ternyata cukup ringkas.
Pengaturan lengkap untuk contoh kita terlihat seperti ini ArrayList<Fragment> fragments = new ArrayList<>(); fragments.add(hello_fragment); fragments.add(small_picture_fragment); fragments.add(big_picture_fragment); SharedElementPageTransformer transformer = new SharedElementPageTransformer(this, fragments); transformer.addSharedTransition(R.id.smallPic_image_cat2, R.id.bigPic_image_cat, true); transformer.addSharedTransition(R.id.smallPic_text_label3, R.id.bigPic_text_label, true); transformer.addSharedTransition(R.id.hello_text, R.id.smallPic_text_label3, true); viewPager.setPageTransformer(false, transformer); viewPager.addOnPageChangeListener(transformer);
onClick
, termasuk semua transisi, mungkin terlihat seperti ini:
smallCatImageView.setOnClickListener( v -> activity.viewPager.setCurrentItem(2) );
Agar kode tidak hilang, saya meletakkan perpustakaan di repositori JCenter dan di GitHub . Jadi saya menghubungi dunia opensource. Anda dapat mencobanya di proyek Anda hanya dengan menambahkan
dependencies {
Semua sumber tersedia di GitHub
Kesimpulan
Sekalipun Internet tidak tahu jawabannya, ini tidak berarti tidak. Cari solusi, coba sampai berhasil. Mungkin Anda akan menjadi yang pertama untuk mencapai bagian bawah ini dan membaginya dengan komunitas.