我想隐藏/显示FloatingActionButton
滚动RecyclerView
。
我的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是此布局的父布局。
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()
没有被调用。
答案 0 :(得分:89)
最简单的解决方案:
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);
}
});
答案 1 :(得分:33)
这应该适合你:
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();
}
}
}
});
答案 2 :(得分:14)
好的,这就是你需要的:
首先,由于您的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
答案 3 :(得分:9)
短而简单的解决方案:
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();
}
}
});
答案 4 :(得分:3)
这就是我的方式。这个对我有用!如果您不知道如何实施,可以在此链接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;
}
}
答案 5 :(得分:1)
我创建了 自定义RecyclerView ,其 OnUpDownScrollListener
, OnLeftRightScrollListener
准备就绪:
<强> 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() {}
});
同样可以通过设置
来处理LeftRight滚动setOnLeftRightScrollListener
我希望它可以帮助某人:)
答案 6 :(得分:1)
解决方案位于:F.A.B Hides but Doesn't Show
问题是Android 25.0.x +将视图设置为GONE,这就是侦听器未报告更改的原因。
答案 7 :(得分:1)
如果您不使用协调器布局,并且想要平滑地隐藏和显示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 :(得分:1)
如果您使用的是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_width="wrap_content"
android:layout_height="wrap_content"
...
app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"
... />
答案 9 :(得分:0)
// 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);
}
});
答案 10 :(得分:0)
我在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();
}
}
答案 11 :(得分:0)
行为路径和onNestedScroll(而不是recyclerview监听器)上的所有答案都没有评论在滚动时多次调用onNestedScroll的事实。这意味着child.show()和child.hide()也会被多次调用。尽管show()和hide()旨在处理这种情况,但它们仍会运行大量代码并创建一些对象,这些对象乘以onNestedScroll的次数被调用,导致不必要地创建大量对象。
考虑到这一点,因为我想运行一个不同的动画而不是默认的show()和hide(),我提出了以下行为实现:
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();
}
}
}
答案 12 :(得分:0)
滚动时,浮动操作按钮将隐藏,滚动停止时将显示。
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);
}
});
答案 13 :(得分:-1)
试试这个
session
享受。