Using Google Design Library how to hide FAB button on Scroll down?
Asked Answered
B

9

50

Google have released Design library, I am using

 compile 'com.android.support:design:22.2.1'

However I cant see any code examples of how to use this library and how to animate the FAB bar on scroll. I guess I can listen for scroll events on the ListView and then animate the button myself, but is this not baked into the API (is this not the point of this support library).

Is there examples for this ?

Bitner answered 16/8, 2015 at 18:9 Comment(2)
Here's one guides.codepath.com/android/Floating-Action-ButtonsVirile
Possible duplicate of how to show/hide FAB on scroll Recycler view with coordinator parentRubinstein
B
113

If you're using RecyclerView and you're looking for something simple, you can try this:

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener(){
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy){
            if (dy > 0)
                fabAddNew.hide();
            else if (dy < 0)
                fabAddNew.show();
        }
    });

By replacing 0 with a constant, you can adjust the sensitivity of triggering, providing smoother experience

Beginning answered 14/1, 2016 at 12:28 Comment(8)
What if your RecyclerView is in a Fragment?Wantage
What will be the difference if it is?Beginning
No access to fabAddNew in Fragment?Wantage
@Wantage ((FloatingActionButton) getActivity().findViewById())Casilde
What an awesome answer! simple and doesn't require any layout modification, +1 .Uncinate
How it will work if you use custom Scrolling like EndlessRecyclerViewScrollListenerJeopardous
It is the simplest solution and works with a simple copy paste!Schuyler
It's better to add fabAddNew.isShown() to avoid much works on scroll.Spoke
M
47

Making a component react to scroll events is most easily done with a custom CoordinatorLayout.Behavior, as they receive scroll events automatically when you override onStartNestedScroll().

An example Behavior that hides and shows the FAB on scroll found in this FABAwareScrollingViewBehavior, built on top of cheesesquare:

public class FABAwareScrollingViewBehavior
    extends AppBarLayout.ScrollingViewBehavior {
  public FABAwareScrollingViewBehavior(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  @Override
  public boolean layoutDependsOn(CoordinatorLayout parent,
      View child, View dependency) {
    return super.layoutDependsOn(parent, child, dependency) ||
            dependency instanceof FloatingActionButton;
  }

  @Override
  public boolean onStartNestedScroll(
      final CoordinatorLayout coordinatorLayout, final View child,
      final View directTargetChild, final View target,
      final int nestedScrollAxes) {
    // Ensure we react to vertical scrolling
    return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
            || super.onStartNestedScroll(coordinatorLayout, child,
               directTargetChild, target, nestedScrollAxes);
  }

  @Override
  public void onNestedScroll(
      final CoordinatorLayout coordinatorLayout, final View child,
      final View target, final int dxConsumed, final int dyConsumed,
      final int dxUnconsumed, final int dyUnconsumed) {
    super.onNestedScroll(coordinatorLayout, child, target,
      dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
    if (dyConsumed > 0) {
      // User scrolled down -> hide the FAB
      List<View> dependencies = coordinatorLayout.getDependencies(child);
      for (View view : dependencies) {
        if (view instanceof FloatingActionButton) {
          ((FloatingActionButton) view).hide();
        }
      }
    } else if (dyConsumed < 0) {
      // User scrolled up -> show the FAB
      List<View> dependencies = coordinatorLayout.getDependencies(child);
      for (View view : dependencies) {
        if (view instanceof FloatingActionButton) {
          ((FloatingActionButton) view).show();
        }
      }
    }
  }
}

Where your scrolling view would have app:layout_behavior="com.support.android.designlibdemo.FABAwareScrollingViewBehavior" instead of app:layout_behavior="@string/appbar_scrolling_view_behavior"

However you can replace hide() and show() with any action if you want. Details on how this was done can be found in this post and the follow up post for v22.2.1 and the follow up post for v25.1.0.

Note that this, like all scrolling behaviors of the Design Library, require that your view supports nested scrolling, which currently limits you to NestedScrollView, RecyclerView - ListView and ScrollView only work on API21+ devices.

Miguelmiguela answered 16/8, 2015 at 19:22 Comment(5)
This is the right solution. Just additional explaination: to use it give your android.support.design.widget.FloatingActionButton this attribute: app:layout_behavior="org.example.something.ScrollAwareFABBehavior"Styracaceous
There is a method ViewCompat.setNestedScrollingEnabled(listView, true);Congregational
@Congregational - sure, ViewCompat.setNestedScrollingEnabled() exists, but that just does the version check for you - it is a no-op prior to API 21.Miguelmiguela
after making child hide [child.hide() ] OnNestedScroll is not calling at all,,,any idea ???Lawsuit
@Lawsuit - that's a change in behavior in v25.1.0 - I've updated the code in the answer to do it the right way now.Miguelmiguela
H
12

If you're NOT using a RecycleView (that is, just regular ScrollView) this will do the trick:

mScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
            if (mScrollView.getScrollY() > oldScrollYPostion) {
                fab.hide();
            } else if (mScrollView.getScrollY() < oldScrollYPostion || mScrollView.getScrollY() <= 0) {
                fab.show();
            }
            oldScrollYPostion = mScrollView.getScrollY();
        }
    });

Don't forget to declare:

private int oldScrollYPostion = 0;

inside you class.

Hofmann answered 29/12, 2017 at 18:12 Comment(1)
Thanks, I was looking for a simple ScrollView solution, almost every article is about RecyclerViewsGunner
S
6
app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"
app:layout_anchor="@id/recyclerList"
app:layout_anchorGravity="bottom"

This did the trick for me, both FAB and RecyclerView direct childs on a Coordinator.

Selfhypnosis answered 7/2, 2021 at 17:44 Comment(1)
Despite all overly mixed programmatically solution with more votes I like this way because first it's working and second it's simple.Desmoid
K
4

The @ianhanniballake solution is working fine but the methods onStartNestedScroll() and onNestedScroll() were deprecated. Here is the updated version:

public class FabAwareScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior {

    public FabAwareScrollingViewBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return super.layoutDependsOn(parent, child, dependency) ||
                dependency instanceof FloatingActionButton;
    }

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                       @NonNull View child, @NonNull View directTargetChild,
                                       @NonNull View target, int axes, int type) {
        // Ensure we react to vertical scrolling
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
                super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
    }

    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                  @NonNull View child, @NonNull View target, int dx, int dy,
                                  @NonNull int[] consumed, int type) {
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);

        if (dy > 0) {
            // User scrolled down -> hide the FAB
            List<View> dependencies = coordinatorLayout.getDependencies(child);
            for (View view : dependencies) {
                if (view instanceof FloatingActionButton) {
                    ((FloatingActionButton) view).hide();
                }
            }
        } else if (dy < 0) {
            // User scrolled up -> show the FAB
            List<View> dependencies = coordinatorLayout.getDependencies(child);
            for (View view : dependencies) {
                if (view instanceof FloatingActionButton) {
                    ((FloatingActionButton) view).show();
                }
            }
        }
    }
}

Also there is a very good post by @ianhanniballake on this topic: Intercepting everything with CoordinatorLayout Behaviors

Kassey answered 15/9, 2017 at 18:46 Comment(3)
In which elemnt do i have to set the behaviour?Draghound
You're a life saver. None of the other behaviors worked, but yours did. Thanks so much!Jeffrey
tried this and the fab doesn't come up as a dependency of the layout even though it is a child of coordinator layout so calls to hide/show never happenSelfhypnosis
L
3
recyclerView.setOnFlingListener(new RecyclerView.OnFlingListener() {
    @Override
    public boolean onFling(int velocityX, int velocityY) {
        if (velocityY < 0)
            mScrollCallbacks.showUI();
            //Code to hide the UI, I have  a custom one that slides down the nav  bar and the fab
        else if (velocityY > 0)
            mScrollCallbacks.hideUI();
            //Code to show the UI

        return false;
    }
});

This works very well

Linen answered 7/6, 2018 at 22:57 Comment(0)
T
2

Using CoordinatorLayout is best way. Hoever if you want to attach listener to ListView or RecyclerView you also can do that. I think is more customizable. Here is my example on git hub.

Github Project: Hide FAB(material Library) with listview

enter image description here

Tellford answered 18/12, 2015 at 13:42 Comment(0)
M
1

try this on onScrollStateChanged instead of onScrolled:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        }

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            if (newState == RecyclerView.SCROLL_STATE_IDLE)
                fab.show();
            else
                fab.hide();
            super.onScrollStateChanged(recyclerView, newState);
        }
Mecke answered 28/4, 2020 at 9:27 Comment(0)
A
0

What I did was the following:

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

          @Override
          public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
              if (dy>0){
                fab.hide();
              }else if (dy == 0){

                  fab.show();
              }else{
                  fab.show();
              }
          }

          @Override
          public void onScrollStateChanged(RecyclerView recyclerView, int newState) {

              super.onScrollStateChanged(recyclerView, newState);
          }
      });

Let me know if it works for you as well.

Articulation answered 15/11, 2020 at 13:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.