在 RecyclerView 的滚动上隐藏 FloatingActionButton
Posted
技术标签:
【中文标题】在 RecyclerView 的滚动上隐藏 FloatingActionButton【英文标题】:Hide FloatingActionButton on scroll of RecyclerView 【发布时间】:2016-01-17 10:58:54 【问题描述】:我想在RecyclerView
的滚动条上隐藏/显示FloatingActionButton
。
我的XML
布局:
<android.support.design.widget.CoordinatorLayout
android:layout_
android:layout_ >
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview_eventlist"
android:layout_
android:layout_ />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_createevent"
android:layout_
android:layout_
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 是这个布局的父布局。
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();
将此布局行为用于FloatingActionButton
。
当我看到logcat
时,只有构造函数被调用。 onNestedScroll()
在我滚动列表时不会被调用。
【问题讨论】:
有人找到解决方案了吗? 【参考方案1】:最简单的解决方案:
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);
);
【讨论】:
我已经尝试过使用代码路径指南,但我浪费了一些时间。这是最简单的一个 这会在滚动时隐藏 FAB。但是当您停止滚动时会显示 fab。向下滚动时如何隐藏它并隐藏直到您向上滚动? 这似乎在滚动时隐藏,但如果你飞行它会隐藏并重新出现两次【参考方案2】:这应该适合你:
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();
);
【讨论】:
很好的解决方案,谢谢【参考方案3】:好的,这就是你需要的:
首先,由于您的 FAB 依赖于 RecyclerView
,因此将以下内容添加到您的行为类中:
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency)
if(dependency instanceof RecyclerView)
return true;
return false;
接下来,为了接收onNestedScroll()
呼叫,您需要覆盖这个:
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;
祝你好运!
更新
你的ScrollAwareFABBehavior
应该是这样的:
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();
另外,它使用com.android.support:design:23.0.1
进行了测试
【讨论】:
还是不行。是不是因为我的支持库已经过时了? 如果可能的话,你能给我提供一个可行的例子吗? @Pritam Kadam 查看我的更新答案以获得完整的解决方案。 我们的 ScrollAwareFABBehavior 看起来完全一样,但它仍然无法正常工作,我的支持库版本是 23.1 您的问题一定出在其他地方,也许是 DrawerLayout【参考方案4】:短而简单的解决方案:
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();
);
【讨论】:
感谢@PriyaRajan glade 它帮助了你:)【参考方案5】:如果您使用 Material Components for Android 并且您的 FAB 在 CoordinatorLayout 内,那么您可以使用 layout_behavior
com.google.android.material.behavior.HideBottomViewOnScrollBehavior
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/filter_fab"
android:layout_
android:layout_
...
app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"
... />
【讨论】:
这很好,但请记住,如果您不将小吃店锚定到您的工厂,这将与您的小吃店重叠。【参考方案6】:我就是这样做的。这个对我有用!如果你不知道如何实现,你可以在这个链接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;
【讨论】:
【参考方案7】:如果您没有使用协调器布局,并且想要平滑地隐藏和显示 FAB。并且您想实现自己的逻辑以在向下滚动时隐藏 fab,并在向上滚动时显示它。
那么这里就是kotlin中的解决方案,
声明一个变量,var scrollingDown = false
然后创建一个监听器,
recycler_view_id.addOnScrollListener(object : RecyclerView.OnScrollListener()
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int)
super.onScrolled(recyclerView, dx, dy)
if (scrollingDown && dy >= 0)
scrollingDown = !scrollingDown
id_show_media_fab.startAnimation(
AnimationUtils.loadAnimation(
getApplicationContext(),
R.anim.fab_close
)
)
else if (!scrollingDown && dy < 0)
scrollingDown = !scrollingDown
id_show_media_fab.startAnimation(
AnimationUtils.loadAnimation(
getApplicationContext(),
R.anim.fab_open
)
)
)
创建动画资源文件
fab_open.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="300"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:interpolator="@android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="100%"
android:toXScale="0.9"
android:toYScale="0.9" />
<alpha
android:duration="300"
android:fromAlpha="0.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="1.0" />
</set>
fab_close.xml
just change
android:fromXScale="0.8"
android:fromYScale="0.8"
【讨论】:
【参考方案8】:我创建了一个 自定义 RecyclerView,它已准备好 OnUpDownScrollListener
、OnLeftRightScrollListener
: p>
代码:
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
用法(UpDownScrollListener):
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()
);
同样你可以通过设置来处理左右滚动
setOnLeftRightScrollListener
我希望它可以帮助某人:)
【讨论】:
【参考方案9】:解决办法在:F.A.B Hides but Doesn't Show
问题是 Android 25.0.x+ 将视图设置为 GONE,这就是侦听器不报告更改的原因。
【讨论】:
【参考方案10】:所有在 Behavior 路径和 onNestedScroll(而不是 recyclerview 侦听器)上的答案都没有评论 onNestedScroll 在滚动时将被多次调用的事实。这意味着 child.show() 和 child.hide() 也将被多次调用。尽管 show() 和 hide() 旨在处理这种情况,但它们仍然运行大量代码并创建一些对象,这些对象乘以调用 onNestedScroll 的次数,导致创建了许多不必要的对象。
考虑到这一点,并且因为我想运行不同的动画而不是默认的 show() 和 hide(),我想出了以下 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();
【讨论】:
【参考方案11】:浮动动作按钮在滚动时隐藏,在滚动停止时显示。
recylerview.addOnScrollListener(new RecyclerView.OnScrollListener()
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState)
switch (newState)
case RecyclerView.SCROLL_STATE_IDLE:
addExpenseBtn.show();
break;
default:
addExpenseBtn.hide();
break;
super.onScrollStateChanged(recyclerView, newState);
);
【讨论】:
【参考方案12】://lv = 列表视图
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);
);
【讨论】:
当给出答案总是很好的时候,添加一些解释而不是仅仅发布一个没有任何信息的原始 sn-p。【参考方案13】:我在 RecyclerView.Adapter 的 onBindViewHolder 方法中使用它来将列表中最后一项的底部边距设置为 72dp,以便它会向上滚动到浮动操作按钮上方。
这不需要列表中的虚拟条目。
@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();
【讨论】:
【参考方案14】:有点晚了,但对我有用。
//This only works on Android M and above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
recyclerView.setOnScrollChangeListener(new View.OnScrollChangeListener()
@Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
if (scrollY > oldScrollY && fab.isShown())
fab.hide();
else if (scrollY < oldScrollY && !fab.isShown())
fab.show();
);
当scrollY
大于oldScrollY
时,这意味着用户已经向下滚动,所以我们只需要检查 FAB 是否显示。如果是,我们将其隐藏。
scrollY
小于oldScrollY
表示向上滚动。我们检查 FAB 是否仍然隐藏以显示它。
【讨论】:
请添加更多详细信息以扩展您的答案,例如工作代码或文档引用。【参考方案15】:试试这个
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
super.onScrolled(recyclerView, dx, dy);
//Customize your if statement
if (recyclerView.computeVerticalScrollOffset() > recyclerView.getHeight() * 2)
if (!fab.isShown())
fab.show();
else
fab.hide();
);
享受吧。
【讨论】:
以上是关于在 RecyclerView 的滚动上隐藏 FloatingActionButton的主要内容,如果未能解决你的问题,请参考以下文章
在 RecyclerView/ListView onScroll 上隐藏 ActionBar