Je veux masquer/afficher FloatingActionButton
sur le parchemin de RecyclerView
.
Ma mise en page XML
:
<Android.support.design.widget.CoordinatorLayout
Android:layout_width="match_parent"
Android:layout_height="match_parent" >
<Android.support.v7.widget.RecyclerView
Android:id="@+id/recyclerview_eventlist"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
<Android.support.design.widget.FloatingActionButton
Android:id="@+id/fab_createevent"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_margin="@dimen/fab_margin"
app:layout_anchor="@id/recyclerview_eventlist"
app:layout_anchorGravity="bottom|right|end"
app:layout_behavior="com.eventizon.behavior.ScrollAwareFABBehavior"
Android:src="@drawable/ic_edit"
app:backgroundTint="@color/custom_color_1"
app:borderWidth="0dp" />
</Android.support.design.widget.CoordinatorLayout>
DrawerLayout est la mise en page parent de cette mise en page.
public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {
private static final String TAG = "ScrollAwareFABBehavior";
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super();
Log.e(TAG,"ScrollAwareFABBehavior");
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout,
FloatingActionButton child, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
// TODO Auto-generated method stub
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed);
Log.e(TAG,"onNestedScroll called");
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
Log.e(TAG,"child.hide()");
child.hide();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
Log.e(TAG,"child.show()");
child.show();
}
}
}
Utilisé ce comportement de mise en page pour FloatingActionButton
.
Quand je vois logcat
, seul le constructeur est appelé. onNestedScroll()
ne s'appelle pas lorsque je fais défiler la liste.
Ok, voici ce dont vous avez besoin:
Tout d'abord, puisque votre FAB dépend de la RecyclerView
, ajoutez ce qui suit à votre classe de comportement:
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
if(dependency instanceof RecyclerView)
return true;
return false;
}
Ensuite, pour recevoir des appels onNestedScroll()
, vous devez remplacer ceci:
public boolean onStartNestedScroll(CoordinatorLayout parent, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
//predict whether you will need to react to the RecyclerView's scroll;
//if yes, return true, otherwise return false to avoid future calls
//of onNestedScroll()
return true;
}
Bonne chance!
Mettre à jour
Voici à quoi votre ScrollAwareFABBehavior
devrait ressembler:
public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {
private static final String TAG = "ScrollAwareFABBehavior";
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super();
}
public boolean onStartNestedScroll(CoordinatorLayout parent, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
return true;
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
if(dependency instanceof RecyclerView)
return true;
return false;
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout,
FloatingActionButton child, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
// TODO Auto-generated method stub
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
child.hide();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
child.show();
}
}
}
En outre, il a été testé avec com.Android.support:design:23.0.1
La solution la plus simple:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
{
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
if (dy > 0 ||dy<0 && fab.isShown())
{
fab.hide();
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState)
{
if (newState == RecyclerView.SCROLL_STATE_IDLE)
{
fab.show();
}
super.onScrollStateChanged(recyclerView, newState);
}
});
Cela devrait fonctionner pour vous:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx,int dy){
super.onScrolled(recyclerView, dx, dy);
if (dy >0) {
// Scroll Down
if (fab.isShown()) {
fab.hide();
}
}
else if (dy <0) {
// Scroll Up
if (!fab.isShown()) {
fab.show();
}
}
}
});
Solution simple et courte:
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0 && mFloatingActionButton.getVisibility() == View.VISIBLE) {
mFloatingActionButton.hide();
} else if (dy < 0 && mFloatingActionButton.getVisibility() !=View.VISIBLE) {
mFloatingActionButton.show();
}
}
});
C'est comme ça que j'ai fait. Ça marche pour moi! Si vous ne savez pas comment mettre en œuvre, vous pouvez voir les détails dans ce lien https://guides.codepath.com/Android/floating-action-buttons
public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {
public ScrollAwareFABBehavior(Context context, AttributeSet attributeSet){
super();
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
child.hide();
} else if (dyConsumed < 0 && child.getVisibility() == View.GONE) {
child.show();
}
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
}
J'ai créé uncustom RecyclerViewqui aOnUpDownScrollListener
,OnLeftRightScrollListener
ready:
MBRecyclerView.Java
public class MBRecyclerView extends RecyclerView {
private OnScrollListener wrappedUpDownScrollListener;
private OnScrollListener wrappedLeftRightScrollListener;
public MBRecyclerView(Context context) {
super(context);
init();
}
public MBRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MBRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
}
// region Scrolling Listener for Up, Down, Left and Right
public void setOnUpDownScrollListener(final OnUpDownScrollListener onUpDownScrollListener) {
if (wrappedUpDownScrollListener == null) {
wrappedUpDownScrollListener = getWrappedUpDownScrollListener(onUpDownScrollListener);
addOnScrollListener(wrappedUpDownScrollListener);
}
}
public void removeOnUpDownScrollListener() {
if (wrappedUpDownScrollListener != null) {
removeOnScrollListener(wrappedUpDownScrollListener);
wrappedUpDownScrollListener = null;
}
}
public void setLeftOnRightScrollListener(final OnLeftRightScrollListener onLeftRightScrollListener) {
if (wrappedLeftRightScrollListener == null) {
wrappedLeftRightScrollListener = getWrappedLeftRightScrollListener(onLeftRightScrollListener);
addOnScrollListener(wrappedLeftRightScrollListener);
}
}
public void removeOnLeftRightScrollListener() {
if (wrappedLeftRightScrollListener != null) {
removeOnScrollListener(wrappedLeftRightScrollListener);
wrappedLeftRightScrollListener = null;
}
}
private OnScrollListener getWrappedUpDownScrollListener(final OnUpDownScrollListener onUpDownScrollListener) {
return new OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (onUpDownScrollListener != null) {
// Negative to check scrolling up, positive to check scrolling down
if (!recyclerView.canScrollVertically(-1)) {
onUpDownScrollListener.onScrolledToTop();
} else if (!recyclerView.canScrollVertically(1)) {
onUpDownScrollListener.onScrolledToBottom();
}
if (dy > 0) {
onUpDownScrollListener.onScrollDown(dy);
} else if (dy < 0) {
onUpDownScrollListener.onScrollUp(dy);
}
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (onUpDownScrollListener != null) {
onUpDownScrollListener.onScrollStopped();
}
}
}
};
}
private OnScrollListener getWrappedLeftRightScrollListener(final OnLeftRightScrollListener onLeftRightScrollListener) {
return new OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (onLeftRightScrollListener != null) {
// Negative to check scrolling left, positive to check scrolling right
if (!recyclerView.canScrollHorizontally(-1)) {
onLeftRightScrollListener.onScrolledToMostLeft();
} else if (!recyclerView.canScrollVertically(1)) {
onLeftRightScrollListener.onScrolledToMostRight();
}
if (dy > 0) {
onLeftRightScrollListener.onScrollRight(dx);
} else if (dy < 0) {
onLeftRightScrollListener.onScrollLeft(dx);
}
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (onLeftRightScrollListener != null) {
onLeftRightScrollListener.onScrollStopped();
}
}
}
};
}
public abstract class OnUpDownScrollListener {
public void onScrollUp(int dy) {}
public void onScrollDown(int dy) {}
public void onScrolledToTop() {}
public void onScrolledToBottom() {}
public void onScrollStopped() {}
}
public abstract class OnLeftRightScrollListener {
public void onScrollLeft(int dx) {}
public void onScrollRight(int dx) {}
public void onScrolledToMostRight() {}
public void onScrolledToMostLeft() {}
public void onScrollStopped() {}
}
// endregion
}
mbRecyclerView.setOnUpDownScrollListener(new MBRecyclerView.OnUpDownScrollListener() {
@Override
public void onScrollUp(int dy) {
// show
}
@Override
public void onScrollDown(int dy) {
// hide
}
// aditional functions:
public void onScrolledToTop() {}
public void onScrolledToBottom() {}
public void onScrollStopped() {}
});
de même, vous pouvez gérer le défilement LeftRight en définissant
setOnLeftRightScrollListener
J'espère que ça peut aider quelqu'un :)
La solution est dans: F.A.B se cache mais ne montre pas
Le problème est que Android 25.0.x + définit la vue sur GONE et que l’auditeur ne signale pas les modifications.
Toutes les réponses figurant dans le chemin Behavior et dans onNestedScroll (au lieu de l'écouteur recyclerview) ne font aucun commentaire sur le fait que onNestedScroll sera appelé plusieurs fois lors du défilement. Cela signifie que child.show () et child.hide () seront également appelés plusieurs fois. Bien que show () et hide () soient conçus pour gérer ce scénario, ils exécutent toujours beaucoup de code et créent des objets qui, multipliés par le temps appelé onNestedScroll, entraînent la création de nombreux objets inutilement.
Compte tenu de cela et parce que je voulais exécuter une animation différente au lieu de la méthode par défaut show () et hide (), j'ai proposé l'implémentation suivante de Behavior:
public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
private static final String TAG = "ScrollAwareFABBehavior";
private boolean fabAnimationStarted = false;
private boolean flingHappened = false;
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
if (target instanceof RecyclerView) {
return true;
}
return false;
}
@Override
public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull final FloatingActionButton child, @NonNull View target, int type) {
super.onStopNestedScroll(coordinatorLayout, child, target, type);
// If animation didn't start, we don't need to care about running the restore animation.
// i.e.: when the user swipes to another tab in a viewpager. The onNestedPreScroll is never called.
if (!fabAnimationStarted) {
return;
}
// Animate back when the fling ended (TYPE_NON_TOUCH)
// or if the user made the touch up (TYPE_TOUCH) but the fling didn't happen.
if (type == ViewCompat.TYPE_NON_TOUCH || (type == ViewCompat.TYPE_TOUCH && !flingHappened)) {
ViewCompat.animate(child).translationY(0).start();
fabAnimationStarted = false;
}
}
@Override
public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, float velocityX, float velocityY, boolean consumed) {
// We got a fling. Flag it.
flingHappened = true;
return false;
}
@Override
public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
if (!fabAnimationStarted) {
Log.d(TAG, "onStartNestedScroll: animation is starting");
fabAnimationStarted = true;
flingHappened = false;
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
ViewCompat.animate(child).translationY(child.getHeight() + lp.bottomMargin).start();
}
}
}
// lv = ListView
lv.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
fab.setVisibility(view.getFirstVisiblePosition() == 0 ? View.VISIBLE : View.INVISIBLE);
}
});
Je l'ai utilisé dans la méthode onBindViewHolder de RecyclerView.Adapter pour définir la marge inférieure du dernier élément de la liste sur 72dp afin qu'il défile au-dessus du bouton d'action flottant.
Cela ne nécessite pas une entrée factice dans la liste.
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// other binding code goes here.
if (position + 1 == getItemCount()) {
// set bottom margin to 72dp.
setBottomMargin(holder.itemView, (int) (72 * Resources.getSystem().getDisplayMetrics().density));
} else {
// reset bottom margin back to zero. (your value may be different)
setBottomMargin(holder.itemView, 0);
}
}
public static void setBottomMargin(View view, int bottomMargin) {
if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
view.requestLayout();
}
}