Es gibt keine traurigere Geschichte auf der Welt
als die Geschichte von ViewPager'e und SET'e
Ich möchte warnen, dass der Autor ein Android-Neuling ist, daher enthält der Artikel so viele technische Ungenauigkeiten, dass Sie eher gewarnt werden sollten, dass technisch zuverlässige Aussagen im Artikel erscheinen könnten.
Wohin das Backend führt
Mein ganzes Leben lang habe ich das Backend gesehen. Anfang 2019 gibt es bereits ein sehr ehrgeiziges, aber noch nicht abgeschlossenes Projekt. Eine fruchtlose Reise nach Zürich für ein Interview mit einer Suchfirma. Winter, Dreck, keine Stimmung. Es gibt keine Kraft und keinen Wunsch, das Projekt weiter voranzutreiben.
Ich wollte dieses schreckliche Backend für immer vergessen. Zum Glück kam mir das Schicksal auf die Idee - es war eine mobile Anwendung. Das Hauptmerkmal war die nicht standardmäßige Verwendung der Kamera. Die Arbeit begann zu kochen. Es hat ein wenig gedauert und jetzt ist der Prototyp fertig. Die Veröffentlichung des Projekts rückte näher und alles war in Ordnung und gut proportioniert, bis ich mich entschied, den Benutzer „ bequem “ zu machen.
Niemand möchte 2019 auf die kleinen Menüschaltflächen klicken, ganz rechts und links möchte er über Bildschirme wischen. Es wird gesagt - getan, getan - gebrochen. Der erste ViewPager
erschien also in meinem Projekt (ich habe einige Begriffe für dasselbe Backend wie ich entschlüsselt - bewegen Sie einfach den Cursor). Und der Shared Element Transition (im Folgenden SET oder Transition) - das Signaturelement von Material Design - weigerte sich rundweg, mit ViewPager
zu arbeiten, und ließ mir die Wahl: entweder Wischen oder schöne Übergangsanimationen zwischen Bildschirmen. Ich wollte weder den einen noch den anderen ablehnen. Also begann meine Suche.
Stunden des Studiums: Dutzende von Themen in Foren und unbeantwortete Fragen zu StackOverflow. Unabhängig davon, was ich öffne, wurde mir angeboten, von RecyclerView zu ViewPager zu wechseln oder „eine Wegerich Fragment.postponeEnterTransition()
anzuhängen“.
Volksheilmittel halfen nicht, und ich beschloss, ViewPager
und Shared Element Transition
selbst in Einklang zu bringen.
Ich begann zu überlegen: "Das Problem tritt in dem Moment auf, in dem der Benutzer von einer Seite zur nächsten wechselt ...". Und dann wurde mir klar: „Sie werden während des Seitenwechsels keine Probleme mit SET haben, wenn Sie die Seite nicht wechseln.“
Wir können den Übergang auf derselben Seite durchführen und dann einfach die aktuelle Seite durch die Zielseite im ViewPager
.
Erstellen Sie zunächst Fragmente, mit denen wir arbeiten werden.
SmallPictureFragment small_picture_fragment = new SmallPictureFragment(); BigPictureFragment big_picture_fragment = new BigPictureFragment();
Versuchen wir, das Fragment auf der aktuellen Seite in etwas anderes zu ändern.
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Wir starten die Anwendung und ... wechseln reibungslos zu einem leeren Bildschirm. Was ist der Grund?
Es stellt sich heraus, dass der Container jeder Seite ViewPager
selbst ist, ohne Zwischenhändler wie Page1Container
, Page2Container
. Wenn das Wechseln einer Seite zu einer anderen nicht funktioniert, wird der gesamte pager
ersetzt.
Um den Inhalt jeder Seite einzeln zu ändern, erstellen wir für jede Seite mehrere Containerfragmente.
RootSmallPictureFragment root_small_pic_fragment = new RootSmallPictureFragment(); RootBigPictureFragment root_big_pic_fragment = new RootBigPictureFragment();
Etwas wird nicht wieder anfangen.
java.lang.IllegalStateException: Container-ID des Fragments BigPictureFragment {...} kann nicht geändert werden: war 2131165289 jetzt 2131165290
Wir können kein Fragment der zweiten Seite ( BigPictureFragment
) an die erste Seite BigPictureFragment
, da es bereits an den Container der zweiten Seite angehängt ist.
Nachdem wir unsere Zähne zusammengebissen haben, fügen wir weitere Doppelfragmente hinzu.
SmallPictureFragment small_picture_fragment_fake = new SmallPictureFragment(); BigPictureFragment big_picture_fragment_fake = new BigPictureFragment();
Verdient! Der Übergangscode, den ich einmal aus den GitHub-Erweiterungen kopiert habe, enthielt bereits Ein- und Ausblendanimationen . Daher verschwanden vor dem Übergang alle statischen Elemente aus dem ersten Fragment, dann wurden die Bilder verschoben, und erst dann wurden die Elemente der zweiten Seite angezeigt. Für den Benutzer sieht dies wie eine echte Bewegung zwischen den Seiten aus.
Alle Animationen sind vergangen, aber es gibt ein Problem. Der Benutzer befindet sich noch auf der ersten Seite, sollte sich aber auf der zweiten befinden.
Um dies zu beheben, ersetzen wir die sichtbare ViewPager
Seite sorgfältig durch eine zweite. Und dann stellen wir den Inhalt der ersten Seite in ihren ursprünglichen Zustand zurück.
handler.postDelayed( () -> {
Was ist das Ergebnis? (Animation - 2,7 mb) Gesamter Quellcode | 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 |
| ); |
| |
| |
| } |
| } |
| |
| } |
Das Projekt kann auf GitHub angesehen werden .
Zusammenfassend. Der Code sah viel solider aus: Anstelle von 2 Anfangsfragmenten erhielt ich bis zu 6 Anweisungen, die die Leistung kontrollierten und die Fragmente zum richtigen Zeitpunkt ersetzten. Und das ist nur in der Demo.
Im selben Projekt wurden nacheinander im Code Sicherungen an den unerwartetsten Stellen angezeigt. Sie erlaubten nicht, dass die Anwendung auseinanderfiel, wenn der Benutzer auf Schaltflächen auf den „falschen“ Seiten klickte oder die Hintergrundarbeit doppelter Fragmente einschränkte.
Es stellte sich heraus, dass das Android keine Rückrufe hat, um den Übergang abzuschließen, und seine Ausführungszeit ist sehr willkürlich und hängt von vielen Faktoren ab (zum Beispiel, wie schnell RecyclerView
in das resultierende Fragment RecyclerView
). Dies führte dazu, dass die Substitution von Fragmenten in handler.postDelayed()
häufig zu früh oder zu spät ausgeführt wurde, was das vorherige Problem nur verschlimmerte.
Das letzte Highlight war, dass der Benutzer während der Animation einfach zu einer anderen Seite wischen und zwei Doppelbildschirme ansehen konnte. Danach zog die Anwendung sie auch auf den gewünschten Bildschirm.
Interessante Artefakte dieses Ansatzes (Animation - 2,7 mb) Dieser Zustand passte nicht zu mir, und ich begann voller gerechter Wut nach einer anderen Lösung zu suchen.
PageTransformer versuchen
Im Internet gab es noch keine Antworten, und ich dachte: Wie kann dieser Übergang noch beschleunigt werden? Etwas im Subkortex des Bewusstseins flüsterte mir zu: "Benutze PageTransformer
, Luke." Die Idee schien mir vielversprechend und ich beschloss zuzuhören.
Die Idee ist, PageTransformer
zu PageTransformer
, der im Gegensatz zu Android SET nicht mehrere Wiederholungen von setTransitionName(transitionName)
und FragmentTransaction.addSharedElement(sharedElement,name)
auf beiden Seiten des Übergangs erfordert. Es verschiebt die Elemente nach dem Wischen und hat eine einfache Oberfläche des Formulars:
public void addSharedTransition(int fromViewId, int toViewId)
Wir fahren mit der Entwicklung fort. Ich werde die Daten von der Methode addSharedTransition(fromId, toId)
um sie aus dem addSharedTransition(fromId, toId)
und sie in der PageTransfomer
Methode PageTransfomer
public void transformPage(@NonNull View page, float position)
Im Inneren gehe ich alle gespeicherten View
, zwischen denen ich eine Animation erstellen muss. Und ich werde versuchen, sie so zu filtern, dass nur sichtbare Elemente animiert werden.
Lassen Sie uns zunächst überprüfen, ob die Elemente, die animiert werden müssen, erstellt wurden. Wir sind nicht wählerisch, und wenn die View
nicht vor dem Start der Animation erstellt wurde, wird die gesamte Animation (wie der Übergang für gemeinsam genutzte Elemente) nicht unterbrochen , sondern beim Erstellen des Elements übernommen.
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) {
Ich finde die Seiten, zwischen denen die Bewegung stattfindet (wie ich die Seitenzahl bestimme, wird unten beschrieben).
View fromPage = pages.get(fromPageNumber); View toPage = pages.get(toPageNumber);
Wenn beide Seiten bereits erstellt wurden, suche ich nach zwei View
, die ich animieren muss.
If (fromPage != null && toPage != null) { fromView = fromPage.findViewById(fromViewId); toView = toPage.findViewById(toViewId);
Zu diesem Zeitpunkt haben wir Ansicht ausgewählt, die auf den Seiten liegt, zwischen denen der Benutzer blättert.
Es ist Zeit, viele Variablen zu erhalten. Ich berechne Referenzpunkte:
Im letzten Snippet habe ich slideToTheRight
, und bereits darin wird es für mich nützlich sein. Die Anmeldeübersetzung hängt davon ab, was bestimmt, dass die View
an ihren Platz oder irgendwo außerhalb des Bildschirms fliegt.
float pageWidth = getScreenWidth(); float sign = slideToTheRight ? 1 : -1; float translationY = (deltaY + deltaHeight / 2) * sign * (-position); float translationX = (deltaX + sign * pageWidth + deltaWidth / 2) * sign * (-position);
Interessanterweise erwiesen sich die Versatzformeln für X
und Y
für beide View
auf der Startseite und der resultierenden Seite trotz der unterschiedlichen anfänglichen Versätze als gleich.
Bei der Skalierung funktioniert dieser Trick leider nicht. Sie müssen überlegen, ob diese View
Start- oder Endpunkt der Animation ist.
Es mag jemanden überraschen, aber transformPage(@NonNull View page, float position)
wird oft aufgerufen: für jede zwischengespeicherte Seite (Cache-Größe ist anpassbar). Um die animierte View
mehrmals neu zu zeichnen, ändern wir bei jedem Aufruf von transformPage()
nur die auf der aktuellen page
.
Wir legen die Position und den Maßstab der animierten Elemente fest Wählen Sie Seiten aus, um Animationen zu zeichnen
ViewPager
nicht eilig, Informationen zwischen den ViewPager
Seiten ViewPager
. Wie ich versprochen habe, werde ich Ihnen jetzt sagen, wie wir diese Informationen erhalten. In unserem PageTransformer
implementieren wir eine weitere ViewPager.OnPageChangeListener
Schnittstelle. Nachdem ich die Ausgabe von onPageScrolled()
über System.out.println()
kam ich zu folgender Formel:
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) {
Das ist alles. Wir haben es geschafft! Die Animation überwacht Benutzergesten. Warum zwischen Swipe und Shared Element Transition wählen, wenn Sie alles verlassen können?
Während ich diesen Artikel schrieb, habe ich den Effekt des Verschwindens statischer Elemente hinzugefügt - er ist immer noch sehr grob, daher wurde er nicht zur Bibliothek hinzugefügt.
Sehen Sie, was am Ende passiert ist (Animation - 2,4 mb) Gesamter Quellcode | /** |
| * 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; |
| } |
| } |
Wie sieht die Arbeit mit der Bibliothek aus?
Die Konfiguration fiel ziemlich kurz aus.
Das vollständige Setup für unser Beispiel sieht folgendermaßen aus 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
, einschließlich aller Übergänge, könnte folgendermaßen aussehen:
smallCatImageView.setOnClickListener( v -> activity.viewPager.setCurrentItem(2) );
Damit der Code nicht verschwindet, habe ich die Bibliothek im JCenter- Repository und auf GitHub abgelegt . Also habe ich mich mit der OpenSource-Welt in Verbindung gesetzt. Sie können es in Ihrem Projekt ausprobieren, indem Sie es einfach hinzufügen
dependencies {
Alle Quellen sind auf GitHub verfügbar
Fazit
Auch wenn das Internet die Antwort nicht kennt, bedeutet dies nicht, dass dies nicht der Fall ist. Suchen Sie nach Problemumgehungen und versuchen Sie es, bis es funktioniert. Vielleicht sind Sie der Erste, der dem auf den Grund geht und es mit der Community teilt.