Bild nach oben schieben mit Official Support Library 23.x.+ bottomSheet wie Google Maps
Lesezeit: 13 Minuten
MiguelHincapieC
Aktualisieren
Ich möchte das gleiche Verhalten erreichen, das Google Maps hat mit Support Library 23.x.+ und ohne JEDE dritte Bibliothek
HINWEIS: Dies ist keine doppelte Frage, weil:
Ich möchte Behaviors, Support Library und ohne JEDE Bibliothek von Drittanbietern verwenden (ich habe es im Titel der Frage und in der obigen Beschreibung hinzugefügt).
ich wollte ALLE Verhaltensweisen die Sie im nächsten GIF sehen, die anderen Fragen fragen nach ein oder zwei Verhaltensweisen und verwenden sie trotzdem, um sie zu erreichen.
Ich habe bereits das offizielle BottomSheet, das funktioniert (sogar innerhalb eines Registerkarten- und Ansichtspagers).
Was macht mich verrückt Wie erreicht man das Bildverhalten, das vom BottomSheet kommt, wenn man mit dem offiziellen BottomSheet nach oben rutscht?.
Ich habe versucht, Anker wie FAB ohne Erfolg zu verwenden.
Ich habe etwas über die Verwendung eines Scroll-Listeners gelesen, aber Leute sagten, es sei nicht reibungslos und schneller wie Google Maps.
Mein XML (ich glaube nicht, dass es helfen wird, aber trotzdem):
Ich werde nur das erste fragen, was mir in den Sinn kam – haben Sie versucht, es zu verwenden? android:animateLayoutChanges="true" ? Wenn das Sinn macht.
– AL.
20. Mai 2016 um 2:13 Uhr
Nö, habe ich nicht probiert. Morgen werde ich mir ansehen, was du gesagt hast, und ich werde dir ein Feedback geben.
– MiguelHincapieC
20. Mai 2016 um 3:35 Uhr
@McAwesomville, es scheint, als sollten Sie es in der Liste verwenden, aber ich habe keine Ahnung (übrigens niemand), ob sie es mit dem Collapsing- und Coordinator-Layout erreichen oder ob sie das verwendet haben, was Sie vorschlagen. Das einzige, was ich herausgefunden habe, ist, dass es kein einzelnes Bild ist, sondern ein AppBarLayout mit Koordinator und Bild darin.
– MiguelHincapieC
22. Mai 2016 um 0:36 Uhr
haha, ich habe mich über dasselbe gewundert (der Bild-Slide-up-Teil – ich hatte Bottomsheet + Coordinatorlayout mit CollapsingToolbarLayout, das einige Parallaxen verursacht, aber auf andere Weise als bei Google Maps) – dann habe ich Ihren Code auf Github gefunden, der funktioniert, und diese Frage
– glücklich
14. November 2016 um 9:31 Uhr
MiguelHincapieC
Wenn Sie es mit Support Library 23.4.0.+ erreichen möchten, werde ich Ihnen sagen, wie ich es bekommen habe und wie es funktioniert.
Soweit ich sehen kann, hat diese Aktivität/Fragment die folgenden Verhaltensweisen:
2 Symbolleisten mit Animationen, die auf die Bewegungen des unteren Blatts reagieren.
Ein FAB, das sich versteckt, wenn es sich in der Nähe der “modalen Symbolleiste” befindet (die erscheint, wenn Sie nach oben gleiten).
Ein Hintergrundbild hinter dem unteren Blatt mit einer Art Parallax-Effekt.
Ein Titel (TextView) in der Symbolleiste, der angezeigt wird, wenn das unterste Blatt ihn erreicht.
Die Benachrichtigungsstatusleiste kann ihren Hintergrund transparent oder vollfarbig machen.
Ein benutzerdefiniertes unteres Blattverhalten mit einem „Anker“-Zustand.
Anmerkung 2: Diese Antwort spricht über 6 Dinge, nicht über 1 oder 2 wie bei anderen Fragen. kannst du jetzt den unterschied sehen?
Ok, jetzt lass uns eins auf Wiedersehen überprüfen:
Symbolleisten
Wenn Sie diese Ansicht in Google Maps öffnen, sehen Sie eine Symbolleiste, in der Sie suchen können. Es ist die einzige, die ich nicht wie Google Maps mache, weil ich es allgemeiner machen wollte. Jedenfalls das ToolBar ist in einem AppBarLayout und es wurde ausgeblendet, wenn Sie mit dem Ziehen des BottomSheet beginnen, und es wird wieder angezeigt, wenn das BottomSheet das erreicht COLLAPSED Zustand.
Um es zu erreichen, benötigen Sie:
ein … kreieren Behavior und verlängern Sie es von AppBarLayout.ScrollingViewBehavior
überschreiben layoutDependsOn und onDependentViewChanged Methoden. Dabei hören Sie auf Bewegungen von bottomSheet.
Erstellen Sie einige Methoden zum Ein- und Ausblenden des AppBarLayout/ToolBar mit Animationen.
So habe ich es für die erste Symbolleiste oder ActionBar gemacht:
Die zweite Symbolleiste oder “modale” Symbolleiste:
Sie müssen einige Methoden überschreiben, aber in dieser müssen Sie sich um mehr Verhalten kümmern:
Symbolleiste mit Animationen ein-/ausblenden
Farbe/Hintergrund der Statusleiste ändern
Anzeigen/Verbergen des BottomSheet-Titels in der Symbolleiste
Schließen Sie das bottomSheet oder senden Sie es in einen reduzierten Zustand
Der Code für diesen ist ein wenig umfangreich, also lasse ich es die Verbindung
Das FAB
Dies ist ebenfalls ein benutzerdefiniertes Verhalten, erstreckt sich jedoch von FloatingActionButton.Behavior. Im onDependentViewChanged Sie müssen schauen, wann es den “offSet” oder Punkt erreicht, an dem Sie es verstecken möchten. In meinem Fall möchte ich es ausblenden, wenn es sich in der Nähe der zweiten Symbolleiste befindet, also suche ich im FAB-Elternteil (einem CoordiantorLayout) nach dem AppBarLayout, das die ToolBar enthält, und verwende dann die ToolBar-Position wie OffSet:
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
if (offset == 0)
setOffsetValue(parent);
if (dependency.getY() <=0)
return false;
if (child.getY() <= (offset + child.getHeight()) && child.getVisibility() == View.VISIBLE)
child.hide();
else if (child.getY() > offset && child.getVisibility() != View.VISIBLE)
child.show();
return false;
}
Das Bild hinter dem BottomSheet mit Parallax-Effekt:
Wie bei den anderen ist es ein benutzerdefiniertes Verhalten, das einzig “Komplizierte” an diesem ist der kleine Algorithmus, der das Bild am BottomSheet verankert hält und das Zusammenbrechen des Bildes wie beim Standard-Parallaxeneffekt vermeidet:
[complete file for backdrop Image with parallax effect][4]
Nun zum Schluss: Das benutzerdefinierte BottomSheet-Verhalten
Um die 3 Schritte zu erreichen, müssen Sie zuerst verstehen, dass das Standard-BottomSheetBehavior 5 Zustände hat: STATE_DRAGGING, STATE_SETTLING, STATE_EXPANDED, STATE_COLLAPSED, STATE_HIDDENund für das Verhalten von Google Maps müssen Sie einen mittleren Zustand zwischen reduziert und erweitert hinzufügen: STATE_ANCHOR_POINT.
Ich habe versucht, das standardmäßige bottomSheetBehavior ohne Erfolg zu erweitern, also kopiere ich einfach den gesamten Code und ändere, was ich brauche.
Um das zu erreichen, wovon ich spreche, folgen Sie den nächsten Schritten:
Erstellen Sie eine Java-Klasse und erweitern Sie sie von CoordinatorLayout.Behavior<V>
Kopieren Sie den Einfügecode aus dem Standard BottomSheetBehavior Datei zu Ihrer neuen.
Ändern Sie die Methode clampViewPositionVertical mit folgendem Code:
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
}
int constrain(int amount, int low, int high) {
return amount < low ? low : (amount > high ? high : amount);
}
Fügen Sie einen neuen Zustand hinzu
öffentlich statisch final int STATE_ANCHOR_POINT = X;
Ändern Sie die nächsten Methoden: onLayoutChild, onStopNestedScroll, BottomSheetBehavior<V> from(V view) und setState (Optional)
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
// First let the parent lay it out
if (mState != STATE_DRAGGING && mState != STATE_SETTLING) {
if (ViewCompat.getFitsSystemWindows(parent) &&
!ViewCompat.getFitsSystemWindows(child)) {
ViewCompat.setFitsSystemWindows(child, true);
}
parent.onLayoutChild(child, layoutDirection);
}
// Offset the bottom sheet
mParentHeight = parent.getHeight();
mMinOffset = Math.max(0, mParentHeight - child.getHeight());
mMaxOffset = Math.max(mParentHeight - mPeekHeight, mMinOffset);
//if (mState == STATE_EXPANDED) {
// ViewCompat.offsetTopAndBottom(child, mMinOffset);
//} else if (mHideable && mState == STATE_HIDDEN...
if (mState == STATE_ANCHOR_POINT) {
ViewCompat.offsetTopAndBottom(child, mAnchorPoint);
} else if (mState == STATE_EXPANDED) {
ViewCompat.offsetTopAndBottom(child, mMinOffset);
} else if (mHideable && mState == STATE_HIDDEN) {
ViewCompat.offsetTopAndBottom(child, mParentHeight);
} else if (mState == STATE_COLLAPSED) {
ViewCompat.offsetTopAndBottom(child, mMaxOffset);
}
if (mViewDragHelper == null) {
mViewDragHelper = ViewDragHelper.create(parent, mDragCallback);
}
mViewRef = new WeakReference<>(child);
mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child));
return true;
}
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
if (child.getTop() == mMinOffset) {
setStateInternal(STATE_EXPANDED);
return;
}
if (target != mNestedScrollingChildRef.get() || !mNestedScrolled) {
return;
}
int top;
int targetState;
if (mLastNestedScrollDy > 0) {
//top = mMinOffset;
//targetState = STATE_EXPANDED;
int currentTop = child.getTop();
if (currentTop > mAnchorPoint) {
top = mAnchorPoint;
targetState = STATE_ANCHOR_POINT;
}
else {
top = mMinOffset;
targetState = STATE_EXPANDED;
}
} else if (mHideable && shouldHide(child, getYVelocity())) {
top = mParentHeight;
targetState = STATE_HIDDEN;
} else if (mLastNestedScrollDy == 0) {
int currentTop = child.getTop();
if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
top = mMinOffset;
targetState = STATE_EXPANDED;
} else {
top = mMaxOffset;
targetState = STATE_COLLAPSED;
}
} else {
//top = mMaxOffset;
//targetState = STATE_COLLAPSED;
int currentTop = child.getTop();
if (currentTop > mAnchorPoint) {
top = mMaxOffset;
targetState = STATE_COLLAPSED;
}
else {
top = mAnchorPoint;
targetState = STATE_ANCHOR_POINT;
}
}
if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
setStateInternal(STATE_SETTLING);
ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState));
} else {
setStateInternal(targetState);
}
mNestedScrolled = false;
}
public final void setState(@State int state) {
if (state == mState) {
return;
}
if (mViewRef == null) {
// The view is not laid out yet; modify mState and let onLayoutChild handle it later
/**
* New behavior (added: state == STATE_ANCHOR_POINT ||)
*/
if (state == STATE_COLLAPSED || state == STATE_EXPANDED ||
state == STATE_ANCHOR_POINT ||
(mHideable && state == STATE_HIDDEN)) {
mState = state;
}
return;
}
V child = mViewRef.get();
if (child == null) {
return;
}
int top;
if (state == STATE_COLLAPSED) {
top = mMaxOffset;
} else if (state == STATE_ANCHOR_POINT) {
top = mAnchorPoint;
} else if (state == STATE_EXPANDED) {
top = mMinOffset;
} else if (mHideable && state == STATE_HIDDEN) {
top = mParentHeight;
} else {
throw new IllegalArgumentException("Illegal state argument: " + state);
}
setStateInternal(STATE_SETTLING);
if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
}
}
public static <V extends View> BottomSheetBehaviorGoogleMapsLike<V> from(V view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
if (!(params instanceof CoordinatorLayout.LayoutParams)) {
throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
}
CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
.getBehavior();
if (!(behavior instanceof BottomSheetBehaviorGoogleMapsLike)) {
throw new IllegalArgumentException(
"The view is not associated with BottomSheetBehaviorGoogleMapsLike");
}
return (BottomSheetBehaviorGoogleMapsLike<V>) behavior;
}
Anmerkung 3: Fügen Sie beim nächsten Mal einen Kommentar hinzu, in dem Sie höflich um eine Änderung der Antwort bitten, oder fragen Sie, warum diese Antwort EINIGES Gleiches enthält als andere Antworten von mir zum selben Thema, BEVOR Sie sie schließen oder als doppelt markieren.
Und so sieht es aus
[]
Manchmal bekomme ich eine transparente Benachrichtigungsleiste, und es sieht schrecklich aus. Irgendeine Idee, wie man es repariert? Bildschirmfoto: imgur.com/a/ng311
– Taha Rushain
3. Dezember 2016 um 12:49 Uhr
Wir arbeiten daran, es zu beheben (ich habe das gleiche Problem). Bleiben Sie dran mit github.
– MiguelHincapieC
3. Dezember 2016 um 16:47 Uhr
@David Ty für das Melden des defekten Links, ich habe ihn repariert. Sagen Sie mir, wenn Sie noch etwas brauchen.
– MiguelHincapieC
28. Dezember 2016 um 18:26 Uhr
@TahaRushain Ich habe es behoben, überprüfe es in GitHub: D
– MiguelHincapieC
11. Januar 2017 um 20:07 Uhr
@AlexBerdnikov Hallo. Google Maps hat zwei Ansichtsmodi, einen, wenn Sie einen Ort ausgewählt haben, und einen anderen, wenn Sie einfach auf eine leere Stelle klicken (diejenige, über die Sie sprechen). Dies kann ausgehend vom Projekt auf GitHub erfolgen, wobei fast alle Verhaltensweisen entfernt werden (Sie finden sie leicht in getrennten Dateien in dieser Link). Der “schwierigste” Teil besteht darin, die STAATEN zu entfernen BottomSheetBehaviorGoogleMapsLike.java
– MiguelHincapieC
31. Mai 2017 um 17:09 Uhr
Glenford Fernandes
Sie können den Effekt mit einem Koordinator-Layout-Verhalten erzielen. Sie müssen eine CoordinatorLayout.Behaviour-Klasse erweitern und eine Abhängigkeit über eine der Ansichten im Koordinator-Layout schreiben, wobei Ihr Bild, das die Ansicht enthält, als untergeordnetes Element beibehalten wird. Um es einfach zu machen, müssen Sie das benutzerdefinierte geschriebene Verhalten an das Bild anhängen, das die Ansicht enthält. Für Unterstützung beim Schreiben benutzerdefinierter Verhaltensweisen folgen Sie bitte dem Link Schreiben von benutzerdefinierten Verhaltensweisen
Sieht so aus, als ob Sie für den richtigen Weg sind, ich habe nur schnell auf den Link geschaut, morgen werde ich ihn gründlich lesen. Aber wenn Sie die “akzeptierte Antwort” wollen, sollten Sie ein gut erklärtes Beispiel oder einen Codeausschnitt geben oder ergänzen, was ich habe 🙂
– MiguelHincapieC
20. Mai 2016 um 3:34 Uhr
Der bereitgestellte Link enthält viel mehr und viele Codeschnipsel, die alles im Detail erklären. Schauen Sie es sich an, es ist leicht zu verstehen.
– Glenford Fernandes
20. Mai 2016 um 3:45 Uhr
Ja, aber wie SO rulez sagte, sollten wir eine vollständige Antwort anstelle eines Links veröffentlichen, da ein Link irgendwann sterben kann.
– MiguelHincapieC
20. Mai 2016 um 5:50 Uhr
Der obige Link gibt mir einige Hinweise, wie ich erreichen kann, was ich will, aber es ist weit davon entfernt, die Antwort / Anleitung dafür zu sein. Tatsächlich hat heute niemand die Antwort darauf xD
– MiguelHincapieC
22. Mai 2016 um 0:28 Uhr
11460500cookie-checkBild nach oben schieben mit Official Support Library 23.x.+ bottomSheet wie Google Mapsyes
Ich werde nur das erste fragen, was mir in den Sinn kam – haben Sie versucht, es zu verwenden?
android:animateLayoutChanges="true"
? Wenn das Sinn macht.– AL.
20. Mai 2016 um 2:13 Uhr
Nö, habe ich nicht probiert. Morgen werde ich mir ansehen, was du gesagt hast, und ich werde dir ein Feedback geben.
– MiguelHincapieC
20. Mai 2016 um 3:35 Uhr
@McAwesomville, es scheint, als sollten Sie es in der Liste verwenden, aber ich habe keine Ahnung (übrigens niemand), ob sie es mit dem Collapsing- und Coordinator-Layout erreichen oder ob sie das verwendet haben, was Sie vorschlagen. Das einzige, was ich herausgefunden habe, ist, dass es kein einzelnes Bild ist, sondern ein AppBarLayout mit Koordinator und Bild darin.
– MiguelHincapieC
22. Mai 2016 um 0:36 Uhr
haha, ich habe mich über dasselbe gewundert (der Bild-Slide-up-Teil – ich hatte Bottomsheet + Coordinatorlayout mit CollapsingToolbarLayout, das einige Parallaxen verursacht, aber auf andere Weise als bei Google Maps) – dann habe ich Ihren Code auf Github gefunden, der funktioniert, und diese Frage
– glücklich
14. November 2016 um 9:31 Uhr