Mettre à jour
Je veux accomplir le même comportement que Google Maps a avec Support Library 23.x. + et sans AUCUNE 3ème bibliothèque
NOTE: ceci n'est pas une question dupliquée parce que:
J'ai déjà la feuille officielle de travail (même dans un onglet et voir un pager).
Ce qui me rend folle comment obtenir le comportement d'image résultant de BottomSheet lorsque vous glissez vers le haut à l'aide de bottomSheet officiel?.
J'ai essayé d'utiliser l'ancre comme FAB sans succès.
J'ai lu quelque chose sur l’utilisation d’un écouteur à défilement, mais ppl a dit que ce n’était ni lisse ni rapide, comme Google Maps .
Mon XML (je ne pense pas que ça va aider mais de toute façon):
<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
xmlns:tools="http://schemas.Android.com/tools"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
tools:context=".ui.MasterActivity">
<Android.support.design.widget.AppBarLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:theme="@style/AppTheme.AppBarOverlay">
<Android.support.v7.widget.Toolbar
Android:id="@+id/toolbar"
Android:layout_width="match_parent"
Android:layout_height="?attr/actionBarSize"
Android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_scrollFlags="scroll|enterAlways|snap">
<Button
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
style="?android:attr/borderlessButtonStyle"
Android:text="Departure"
Android:layout_gravity="center"
Android:id="@+id/buttonToolBar"
/>
</Android.support.v7.widget.Toolbar>
<Android.support.design.widget.TabLayout
Android:id="@+id/tabs"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
app:tabBackground="@Android:color/white"
app:tabTextColor="@color/colorAccent"
app:tabSelectedTextColor="@color/colorAccent"/>
</Android.support.design.widget.AppBarLayout>
<Android.support.v4.view.ViewPager
Android:id="@+id/viewpager"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<Android.support.v4.widget.NestedScrollView
Android:id="@+id/asdf"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical"
app:behavior_peekHeight="100dp"
Android:fitsSystemWindows="true"
app:layout_behavior="Android.support.design.widget.BottomSheetBehavior">
<LinearLayout
Android:id="@+id/qwert"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical"
Android:paddingBottom="16dp"
Android:background="@Android:color/white"
Android:padding="15dp">
<TextView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:text="BOOTOMSHEET TITLE"
Android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<Button
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:text="Button1"/>
<TextView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:text="text 2"
Android:layout_margin="10dp"/>
<TextView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:text="text 3"
Android:layout_margin="10dp"/>
<TextView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:text="text 4"
Android:layout_margin="10dp"/>
<FrameLayout
Android:layout_width="match_parent"
Android:layout_height="320dp"
Android:background="@color/colorAccent">
<TextView
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_gravity="center"
Android:text="Your remaining content here"
Android:textColor="@Android:color/white" />
</FrameLayout>
</LinearLayout>
</Android.support.v4.widget.NestedScrollView>
<Android.support.design.widget.FloatingActionButton
Android:layout_height="wrap_content"
Android:layout_width="wrap_content"
app:layout_anchor="@id/asdf"
app:layout_anchorGravity="top|right|end"
Android:src="@drawable/abc_ic_search_api_mtrl_alpha_copy"
Android:layout_margin="@dimen/fab_margin"
Android:clickable="true"/>
</Android.support.design.widget.CoordinatorLayout>
Si vous souhaitez y parvenir avec Support Library 23.4.0. +, Je vous dirai comment je l’ai obtenue et comment cela fonctionne.
note: je m'excuse pour mon anglais, j'ai essayé de donner une réponse de programmation (juste brève et mélangée avec du code utile) mais j'ai l'impression que ce n'était pas assez bon.
Pour autant que je puisse voir que cette activité/ce fragment a les comportements suivants:
note2: Cette réponse parle de 6 choses pas de 1 ou 2 comme autre question, pouvez-vous voir la différence maintenant?
Ok, maintenant vérifions-en un sur un:
Barres d'outils
Lorsque vous ouvrez cette vue dans Google Maps, vous pouvez voir une barre d’outils dans laquelle vous pouvez effectuer une recherche, c’est le seul qui ne me ressemble pas, comme Google Maps, car je voulais le faire de manière plus générique. Quoi qu'il en soit, cette ToolBar
est dans une AppBarLayout
et elle est cachée lorsque vous commencez à faire glisser la feuille inférieure et elle réapparaît lorsque la feuille inférieure atteint l'état COLLAPSED
.
Pour y parvenir, il vous faut:
Behavior
et l'étendre à partir de AppBarLayout.ScrollingViewBehavior
layoutDependsOn
et onDependentViewChanged
. Ce faisant, vous écouterez les mouvements de la feuille de fond.Voici comment je l'ai fait pour la première barre d'outils ou ActionBar:
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof NestedScrollView;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
if (mChild == null) {
initValues(child, dependency);
return false;
}
float dVerticalScroll = dependency.getY() - mPreviousY;
mPreviousY = dependency.getY();
//going up
if (dVerticalScroll <= 0 && !hidden) {
dismissAppBar(child);
return true;
}
return false;
}
private void initValues(final View child, View dependency) {
mChild = child;
mInitialY = child.getY();
BottomSheetBehaviorGoogleMapsLike bottomSheetBehavior = BottomSheetBehaviorGoogleMapsLike.from(dependency);
bottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, @BottomSheetBehaviorGoogleMapsLike.State int newState) {
if (newState == BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED ||
newState == BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN)
showAppBar(child);
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
private void dismissAppBar(View child){
hidden = true;
AppBarLayout appBarLayout = (AppBarLayout)child;
mToolbarAnimation = appBarLayout.animate().setDuration(mContext.getResources().getInteger(Android.R.integer.config_shortAnimTime));
mToolbarAnimation.y(-(mChild.getHeight()+25)).start();
}
private void showAppBar(View child) {
hidden = false;
AppBarLayout appBarLayout = (AppBarLayout)child;
mToolbarAnimation = appBarLayout.animate().setDuration(mContext.getResources().getInteger(Android.R.integer.config_mediumAnimTime));
mToolbarAnimation.y(mInitialY).start();
}
le fichier complet si vous en avez besoin
La deuxième barre d'outils ou la barre d'outils "Modal":
Vous devez remplacer les mêmes méthodes mais dans celle-ci, vous devez vous préoccuper de plus de comportements:
Le code pour celui-ci est un peu volumineux alors je vais laisser le lien
Le FAB
Ceci est un comportement personnalisé aussi, mais s'étend de FloatingActionButton.Behavior
. Dans onDependentViewChanged
, vous devez regarder quand il atteint le "décalage" ou le point où vous voulez le cacher. Dans mon cas, je veux le masquer quand il est proche de la deuxième barre d'outils. Je creuse donc dans FAB parent (un CoordiantorLayout) à la recherche du AppBarLayout contenant la barre d'outils, puis j'utilise la position de la barre d'outils comme 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;
}
Complétez le lien Custom FAB Behavior
L'image derrière la feuille inférieure avec effet de parallaxe:
Comme les autres, il s’agit d’un comportement personnalisé, la seule chose "compliquée" dans celui-ci est le petit algorithme qui maintient l’image ancrée dans la feuille de fond et évite que l’image ne s’effondre comme un effet de parallaxe par défaut:
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
if (mYmultiplier == 0) {
initValues(child, dependency);
return true;
}
float dVerticalScroll = dependency.getY() - mPreviousY;
mPreviousY = dependency.getY();
//going up
if (dVerticalScroll <= 0 && child.getY() <= 0) {
child.setY(0);
return true;
}
//going down
if (dVerticalScroll >= 0 && dependency.getY() <= mImageHeight)
return false;
child.setY( (int)(child.getY() + (dVerticalScroll * mYmultiplier) ) );
return true;
}
fichier complet pour toile de fond Image avec effet de parallaxe
Maintenant pour la fin: Le comportement personnalisé de BottomSheet
Pour réaliser les 3 étapes, vous devez d'abord comprendre que BottomSheetBehavior par défaut a 5 états: STATE_DRAGGING, STATE_SETTLING, STATE_EXPANDED, STATE_COLLAPSED, STATE_HIDDEN
et pour le comportement de Google Maps, vous devez ajouter un état intermédiaire entre replié et développé: STATE_ANCHOR_POINT
.
J'ai essayé d'étendre le bottomSheetBehavior par défaut sans succès, alors il me suffit de copier coller tout le code et de modifier ce dont j'ai besoin.
Pour réaliser ce dont je parle, suivez les étapes suivantes:
CoordinatorLayout.Behavior<V>
BottomSheetBehavior
dans votre nouveau.Modifiez la méthode clampViewPositionVertical
avec le code suivant:
@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);
}
Ajouter un nouvel état
public static final int STATE_ANCHOR_POINT = X;
Modifiez les méthodes suivantes: onLayoutChild
, onStopNestedScroll
, BottomSheetBehavior<V> from(V view)
et setState
(facultatif)
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;
}
lien vers le projet de trou dans où vous pouvez voir tous les comportements personnalisés
note3: la prochaine fois, ajoutez un commentaire demandant de manière polie de changer la réponse ou demandez-lui pourquoi cette réponse a une substance qui est égale à celle des autres sur le même sujet AVANT de le fermer ou de marquer comme dupliqué.
Et voici à quoi ça ressemble:
[]
Vous pouvez obtenir l'effet en utilisant un comportement de présentation de coordinateur. Vous devrez étendre une classe CoordinatorLayout.Behaviour et écrire une dépendance sur l'une des vues de la présentation du coordinateur, en conservant votre vue contenant l'image comme enfant .. Pour simplifier, vous devez associer le comportement écrit personnalisé à la vue contenant l'image . Pour obtenir de l'aide sur la rédaction de comportements personnalisés, veuillez suivre le lien Écrire des comportements personnalisés