Hiding the ActionBar on RecyclerView/ListView onScroll - android

In my application I got an activity with some kind of actionbar at the top and the listview below it. What I want to do - is to scroll it UP with the list, so it hides and then, when the list is being scrolled down - it should scroll down with the list, like it was just over the upper screen border. how can i achieve this functionality?

Updated 6/3/2015:
Google now supports this using the CoordinatorLayout.
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<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/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</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.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_done" />
</android.support.design.widget.CoordinatorLayout>
Source: https://github.com/chrisbanes/cheesesquare/blob/master/app/src/main/res/layout/include_list_viewpager.xml
Documented here: https://developer.android.com/reference/android/support/design/widget/AppBarLayout.html
Original Answer:
Example similar to Google Play Music and Umano apps:
https://github.com/umano/AndroidSlidingUpPanel
Take a look at the code in this repository. As you slide the panel up, the ActionBar slides up as well.
From the Demo:
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
SlidingUpPanelLayout layout = (SlidingUpPanelLayout) findViewById(R.id.sliding_layout);
layout.setShadowDrawable(getResources().getDrawable(R.drawable.above_shadow));
layout.setAnchorPoint(0.3f);
layout.setPanelSlideListener(new PanelSlideListener() {
#Override
public void onPanelSlide(View panel, float slideOffset) {
Log.i(TAG, "onPanelSlide, offset " + slideOffset);
if (slideOffset < 0.2) {
if (getActionBar().isShowing()) {
getActionBar().hide();
}
} else {
if (!getActionBar().isShowing()) {
getActionBar().show();
}
}
}
#Override
public void onPanelExpanded(View panel) {
Log.i(TAG, "onPanelExpanded");
}
#Override
public void onPanelCollapsed(View panel) {
Log.i(TAG, "onPanelCollapsed");
}
#Override
public void onPanelAnchored(View panel) {
Log.i(TAG, "onPanelAnchored");
}
});
Download example here:
https://play.google.com/store/apps/details?id=com.sothree.umano
ListView - without Libraries:
I recently wanted the same functionality and this works perfectly for me:
As the user scrolls upward, the ActionBar will be hidden in order to give the user the entire screen to work work with.
As the user scrolls downward and lets go, the ActionBar will return.
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
listView.setOnScrollListener(new OnScrollListener() {
int mLastFirstVisibleItem = 0;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) { }
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (view.getId() == listView.getId()) {
final int currentFirstVisibleItem = listView.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
// getSherlockActivity().getSupportActionBar().hide();
getSupportActionBar().hide();
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
// getSherlockActivity().getSupportActionBar().show();
getSupportActionBar().show();
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
});
RecyclerView - without libraries
this.mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
int mLastFirstVisibleItem = 0;
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
final int currentFirstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (currentFirstVisibleItem > this.mLastFirstVisibleItem) {
MainActivity.this.getSupportActionBar().hide();
} else if (currentFirstVisibleItem < this.mLastFirstVisibleItem) {
MainActivity.this.getSupportActionBar().show();
}
this.mLastFirstVisibleItem = currentFirstVisibleItem;
}
});
Let me know if you need anymore help!

You experience the flickering since by hiding/showing the ActionBar the available space for your content layout changes, which causes a relayout. With this the index of the first visible item's position changes as well (you can verify this by logging out mLastFirstVisibleItem and currentFirstVisibleItem.
You can cope with the flickering by letting the ActionBar overlay your content layout. To enable overlay mode for the action bar, you need to create a custom theme that extends an existing action bar theme and set the android:windowActionBarOverlay property to true.
With this you can eliminate the flickering but the action bar will overlay your listview's content. An easy solution to this is to set the listview's (or the root layout's) top padding to the action bar's height.
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?android:attr/actionBarSize" />
Unfortunately this will result in a constant padding at the top. A refinement of this solution is to add a header view to the list view which has the height of ?android:attr/actionBarSize (and remove the top padding set previously)

What you are looking for is called Quick Return pattern, applied to the Action Bar. Google IO 2014 app use exactly that. I use it in one of my apps, you can check the source code of that Google app to see how they got it. The BaseActivity class is where you have what you need, read the code and extract just that specific functionality. Enjoy coding! :)

I am sure that it's not the best solution. But I haven't found a better one yet. Hope it will be helpfull.
static boolean scroll_down;
...
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (scroll_down) {
actionBar.hide();
} else {
actionBar.show();
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 70) {
//scroll down
scroll_down = true;
} else if (dy < -5) {
//scroll up
scroll_down = false;
}
}
});

I have a thought that it is a good idea.
listView.setOnScrollListener(new OnScrollListener() {
int mLastFirstVisibleItem = 0;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_FLING:
if (view.getId()==lv_searchresult_results.getId()){
final int currentFirstVisibleItem=lv_searchresult_results.getFirstVisiblePosition();
if(currentFirstVisibleItem>mLastFirstVisibleItem){
ObjectAnimator.ofFloat(toolbar, "translationY", -toolbar.getHeight()).start();
else if(currentFirstVisibleItem<(mLastFirstVisibleItem)){
ObjectAnimator.ofFloat(toolbar, "translationY", 0).start();
}
mLastFirstVisibleItem= currentFirstVisibleItem;
}
if(lv_searchresult_results.getLastVisiblePosition() == myAdapter.getListMap().size()){
if(myAdapter.getListMap().size() < allRows&&!upLoading){
}
}
break;
case OnScrollListener.SCROLL_STATE_IDLE:
if (view.getFirstVisiblePosition()<=1){
ObjectAnimator.ofFloat(toolbar, "translationY", 0).start();
}
if(lv_searchresult_results.getLastVisiblePosition() == myAdapter.getListMap().size()){
if(myAdapter.getListMap().size() < allRows&&!upLoading){
}
}
break;

I think it will work fine.....
listView.setOnScrollListener(new OnScrollListener() {
int mLastFirstVisibleItem = listView.getLastVisiblePosition();
boolean isScrolling = false;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(scrollState == 0)
isScrolling = false;
else if(scrollState == 1)
isScrolling = true;
else if(scrollState == 2)
isScrolling = true;
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (view.getId() == myTeamListView.getId()) {
if(isScrolling) {
final int currentFirstVisibleItem = myTeamListView.getLastVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
((AppCompatActivity)getActivity()).getSupportActionBar().hide();
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
((AppCompatActivity)getActivity()).getSupportActionBar().show();
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
}
});

If you are using a CoordinatorLayout, here is the trick.
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways" />
The app:layout_scrollFlags="scroll|enterAlways" line will cause our Toolbar to scroll off the screen when the user scrolls down the list and as soon as he starts scrolling up the Toolbar will show up again.

Related

"Back to top" button to be displayed only after scrolling

I have a RecyclerView that display pictures, and have put a FloatingActionButton to let my user go back to the top of the view like such:
// On FAB click, scroll back to the top of the layout
private void handleUp() {
FloatingActionButton FAB = (FloatingActionButton) view.findViewById(R.id.upFAB);
FAB.setOnClickListener(view1 -> {
if (recyclerView != null) {
recyclerView.smoothScrollToPosition(0);
}
});
}
With the following view:
<android.support.design.widget.FloatingActionButton
app:fabSize="mini"
android:id="#+id/upFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#mipmap/up_icon"
android:layout_gravity="bottom|center"
android:layout_marginBottom="60dp" />
It looks like this.
As you can see the button is displayed all the time, how may I only display it when the user scrolls for a certain amount in the RecyclerView?
try this way and tell me , i didn't try it
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
if (firstVisibleItem > 1) {
//Show FAB
}
else{
//Hide FAB
}
}
});
I just did it through ListView.
Here is the example using ListView instead of RecycleView.
Try this, Hope it will help.
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if(firstVisibleItem > 2)
{
upFAB.setVisibility(View.VISIBLE);
}
else
{
upFAB.setVisibility(View.GONE);
}
}
});

Hide FAB in NestedScrollView when scrolling

I am having a nestedscrollview with content like some linearlayouts and textviews.
I am using a floatingactionbutton library for some reasons, as well. So I can't use any behavior for it.
I don't know how I should handle the scrollchangelistener from scrollview to hide and show the fab dynamically like a behavior.
Any suggestions how to hide and show the fab while scrolling?
Simple add this code below to your NestedScrollView ScrollChangeListener:
NestedScrollView nsv = v.findViewById(R.id.nsv);
nsv.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
#Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if (scrollY > oldScrollY) {
fab.hide();
} else {
fab.show();
}
}
});
Create FabScrollBehavior class
public class FabScrollBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
private int toolbarHeight;
public FabScrollBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
this.toolbarHeight = AppUtil.getToolbarHeight(context);
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
return dependency instanceof AppBarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
if (dependency instanceof AppBarLayout) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
int fabBottomMargin = lp.bottomMargin;
int distanceToScroll = fab.getHeight() + fabBottomMargin;
float ratio = (float)dependency.getY()/(float)toolbarHeight;
fab.setTranslationY(-distanceToScroll * ratio);
}
return true;
}
}
Where AppUtil.getToolbarHeight(context) is -
public static int getToolbarHeight(Context context) {
final TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
new int[]{R.attr.actionBarSize});
int toolbarHeight = (int) styledAttributes.getDimension(0, 0);
styledAttributes.recycle();
return toolbarHeight;
}
then in your layout add to FloatingActionButton layout_behavior:
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab_task_accept"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_accepted"
app:layout_behavior="pass.to.your.FabScrollBehavior.Class"
app:theme="#style/Widget.AppTheme.Fab"/>
The whole layout looks like
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/Widget.AppTheme.AppBarOverlay">
<include
layout="#layout/include_layout_toolbar_scroll"/>
</android.support.design.widget.AppBarLayout>
<include layout="#layout/include_layout_content_with_nestedscroll"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab_task_accept"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_accepted"
app:layout_behavior="pass.to.FabScrollBehavior.Class"
app:theme="#style/Widget.AppTheme.Fab"/>
</android.support.design.widget.CoordinatorLayout>
Implemented from https://mzgreen.github.io/2015/02/15/How-to-hideshow-Toolbar-when-list-is-scroling(part1)/
define variable type int in your Activity or fragment to set previous Scroll from ScrollView then use this method to listen change scroll in ScrollView Class
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
// previousScrollY this variable is define in your Activity or Fragment
if (scrollView.getScrollY() > previousScrollY && floatingActionButton.getVisibility() == View.VISIBLE) {
floatingActionButton.hide();
} else if (scrollView.getScrollY() < previousScrollY && floatingActionButton.getVisibility() != View.VISIBLE) {
floatingActionButton.show();
}
previousScrollY = scrollView.getScrollY();
}
});
will work done all version of android
After spending such time i have found the solution for it.
It may work in all situations. Though it is a hack not the proper solution but you can apply it to make this thing work.
As we know setOnScrollChangeListener will only work if minimum api 23, so what if my minimum api level is less then 23.
So I found out solution from stack overflow that we can use getViewTreeObserver().addOnScrollChangedListener for that so this will be compatible solution for all devices.
Now let's move to the final solution of over problem "Hide fab button when nested scroll view scrolling and Show fab button when nested scroll view in ideal state"
So for that we can use Handler with postDelayed to slove this issue.
Define on variable in you context private int previousScrollY = 0;
Then use getViewTreeObserver().addOnScrollChangedListener to your nested scroll view like this.
NESTEDSCROLLVIEW.getViewTreeObserver().addOnScrollChangedListener(new
ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (NESTEDSCROLLVIEW.getScrollY() == previousScrollY) {
FABBUTTON.setVisibility(View.VISIBLE);
} else {
FABBUTTON.setVisibility(View.INVISIBLE);
}
}
}, 10);
previousScrollY = NESTEDSCROLLVIEW.getScrollY();
}
});
Now you are ready to go....
You can use this listener to observe and hide FAB when scrolling.
nestedScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
if (nestedScrollView != null) {
if (nestedScrollView.getChildAt(0).getBottom() <= (nestedScrollView.getHeight() + nestedScrollView.getScrollY())) {
fab.setVisibility(View.INVISIBLE);
} else {
fab.setVisibility(View.VISIBLE);
}
}
}
});

How to hide or show the partially hidden toolbar/actionbar when you stop scrolling the listview/recyclerview in android

So to show and hide the action bar when scrolling I am using this method
using coordinator layout and appbarlayout
But when I stop scrolling the list in the middle of toolbar being hidden or shown it stays there and only part of toolbar is visible.
What I want to do is make the toolbar show or hide completely based on the percentage of toolbar visible.
Is there a way I can acheive this using the coordinator layout and appbarlayout?
EDIT - Just get 23.1.0 design library and add "|snap" attribute to your ToolBar layout:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways|snap />
-----
-----
SO YOU DON'T NEED TO USE THE FOLLOWING CODE:
Have you tried to add the layout_behaviour attribute to AppBarLayout?
app:layout_behavior="AppBarLayoutSnapBehavior"
Then you need to create class "AppBarLayoutSnapBehavior":
public class AppBarLayoutSnapBehavior extends AppBarLayout.Behavior {
private ValueAnimator mAnimator;
private boolean mNestedScrollStarted = false;
public AppBarLayoutSnapBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View directTargetChild, View target, int nestedScrollAxes) {
mNestedScrollStarted = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
if (mNestedScrollStarted && mAnimator != null) {
mAnimator.cancel();
}
return mNestedScrollStarted;
}
#Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
if (!mNestedScrollStarted) {
return;
}
mNestedScrollStarted = false;
int scrollRange = child.getTotalScrollRange();
int topOffset = getTopAndBottomOffset();
if (topOffset <= -scrollRange || topOffset >= 0) {
// Already fully visible or fully invisible
return;
}
if (topOffset < -(scrollRange / 2f)) {
// Snap up (to fully invisible)
animateOffsetTo(-scrollRange);
} else {
// Snap down (to fully visible)
animateOffsetTo(0);
}
}
private void animateOffsetTo(int offset) {
if (mAnimator == null) {
mAnimator = new ValueAnimator();
mAnimator.setInterpolator(new DecelerateInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
setTopAndBottomOffset((int) animation.getAnimatedValue());
}
});
} else {
mAnimator.cancel();
}
mAnimator.setIntValues(getTopAndBottomOffset(), offset);
mAnimator.start();
}
Should work for you.
I've got partial solution. To your recyclerview add onScrollListener bellow, with AppBarLayout in param
public class ImprovedScrollListener extends RecyclerView.OnScrollListener {
private WeakReference<AppBarLayout> mAppBarLayout;
public ImprovedScrollListener(AppBarLayout appBarLayout) {
mAppBarLayout = new WeakReference<>(appBarLayout);
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE && mAppBarLayout.get() != null) {
if (!recyclerView.canScrollVertically(-1) && !recyclerView.canScrollVertically(1)) {
/// TODO: 21.10.2015 handle this state
}else if(!recyclerView.canScrollVertically(-1)){
mAppBarLayout.get().setExpanded(true, true);
} else {
mAppBarLayout.get().setExpanded(false, true);
}
}
}
}
This will work for recyclerviews that can scroll, this still behaves ugly when recyclerview is not scrollable (see TODO in code). If somebody find solution for this case let me know
Setting Toolbar within AppBarLayout and cordinationLayout will itself take care of hiding and showing the actionBar
<?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"
android:layout_width="match_parent"
android:id="#+id/cordinationLayout"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>

FloatingActionButton hide on list scroll

Im using the FloatingActionButton from the android.support.design.widget package:
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_marginBottom="20dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:backgroundTint="#color/primaryColor"
android:src="#drawable/ic_search_white_24dp"
app:borderWidth="0dp"
app:elevation="6dp"
app:backgroundTint="#color/primaryColorDark"
app:rippleColor="#color/accentColor" />
Is it possible to configure that button to hide with an animation when the listview is scrolling down and to show it again when listview is scrolling up to the top?
Those who are looking to make it with recyclerview can do this:
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);
}
});
Sorry! I am late by years to answer this. I hope this still helps someone. This is also my first answer.
Mates! No need to implement scroll listeners.
Add the following to the floating action button xml:
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
giving:
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="#+id/fabAddOItransferIn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
android:text="#string/btn_text_transfer_in"
app:icon="#android:drawable/ic_input_add"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
In response to the following comment of mine,
"Sorry! I just noticed this has a weird side effect. Any snackbars will overlap this floating action button if app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior is added. ☹️ Taking this line off will prevent the overlap and the floating action button will behave as it is intended to inside the coordinator layout. "
To counter this, do use the following:
Snackbar.make(floating_action_button, "Some snackbar text!", BaseTransientBottomBar.LENGTH_SHORT).setAnchorView(floating_action_button).show();
A small improvement to the code from Irfan Raza:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener(){
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy){
if (dy<0 && !fab.isShown())
fab.show();
else if(dy>0 && fab.isShown())
fab.hide();
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
The Floating Action Button hides when scrolling down and shows when scrolling up.
See this. Here it tells how to do what you are trying to achieve. You have to use it like this in a CoordinatorLayout and ListView :
<android.support.design.widget.CoordinatorLayout
android:id="#+id/main_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/lvToDoList"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="#drawable/ic_done"
app:layout_anchor="#id/lvToDoList"
app:layout_anchorGravity="bottom|right|end" />
</android.support.design.widget.CoordinatorLayout>
using this class you can easily animate you FAB, here I have implemented onStopNestedScroll() method to show your Fab whenever scroll stop.
I set 1000 miliSeconds as delay using Handler();
public class FabBehaviour extends CoordinatorLayout.Behavior<FloatingActionButton> {
private static final String TAG = "ScrollingFABBehavior";
Handler mHandler;
public FabBehaviour(Context context, AttributeSet attrs) {
super();
}
public FabBehaviour() {
super();
}
#Override
public void onStopNestedScroll(#NonNull CoordinatorLayout coordinatorLayout, #NonNull final FloatingActionButton child, #NonNull View target, int type) {
super.onStopNestedScroll(coordinatorLayout, child, target, type);
if (mHandler == null)
mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
Log.d("FabAnim", "startHandler()");
}
}, 1000);
}
#Override
public void onNestedScroll(#NonNull CoordinatorLayout coordinatorLayout, #NonNull FloatingActionButton child, #NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
if (dyConsumed > 0) {
Log.d("Scrolling", "Up");
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
int fab_bottomMargin = layoutParams.bottomMargin;
child.animate().translationY(child.getHeight() + fab_bottomMargin).setInterpolator(new LinearInterpolator()).start();
} else if (dyConsumed < 0) {
Log.d("Scrolling", "down");
child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
}
}
#Override
public boolean onStartNestedScroll(#NonNull CoordinatorLayout coordinatorLayout, #NonNull FloatingActionButton child, #NonNull View directTargetChild, #NonNull View target, int axes, int type) {
if (mHandler != null) {
mHandler.removeMessages(0);
Log.d("Scrolling", "stopHandler()");
}
return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
}
your_layout.xml
<android.support.design.widget.FloatingActionButton
android:id="#+id/imageViewYes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end|right"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_yes"
app:backgroundTint="#color/white"
android:scaleType="center"
app:elevation="6dp"
app:fabSize="normal"
app:layout_behavior="com.your.package.FabBehaviour"
app:pressedTranslationZ="12dp"
app:rippleColor="#color/gray" />
hey there is o require to take the recyclerview for auto hiding the floating action button on scrolling down for this purpose we can use default listview with floating action button in normal way only make modifications on listview.onscroll listener then we can get feel like recycle
listview.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int lastItem = firstVisibleItem + visibleItemCount;
if (lastItem == totalItemCount) {
fab.setVisibility(View.INVISIBLE);
}else {
fab.setVisibility(View.VISIBLE);
}
}
});
There is my code in kotlin.
class ScrollAwareFABBehavior (val recyclerView: RecyclerView, val floatingActionButton: FloatingActionButton) {
fun start() {
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0) {
if (floatingActionButton!!.isShown) {
floatingActionButton?.hide()
}
} else if (dy < 0) {
if (!floatingActionButton!!.isShown) {
floatingActionButton?.show()
}
}
}
})
}
}
Now, you just need to call the ScrollAwareFABBehavior with the recyclerView and the fab on constructor, then call method start().
ScrollAwareFABBehavior(recyclerView = recyclerViewPlaceFormContainer, floatingActionButton = floatingActionButton).start()
Another method for recycleView using kotlin extensions.
fun RecyclerView.attachFab(fab : FloatingActionButton) {
this.addOnScrollListener(object : RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0)
fab.hide()
else if (dy < 0)
fab.show()
}
})
}
Now you can attach fab to any recycleView with:
rv.attachFab(requireActivity().fab)
// in my case i made fab public on activity
Here I am adding extra padding for last view item to avoid overlapping list item with floating action button
I used this in a RecyclerView.Adapter's onBindViewHolder method to set the bottom margin of the last item in the list to 72dp so that it will scroll up above the floating action button.
This does not require a dummy entry in the list.
#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();
}
}
Kotlin + DataBinding Adapter
#BindingAdapter("bindAdapter:attachFloatingButton")
fun bindRecyclerViewWithFB(recyclerView: RecyclerView, fb: FloatingActionButton) {
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0 && fb.isShown) {
fb.hide()
} else if (dy < 0 && !fb.isShown) {
fb.show()
}
}
})
}
and the xml
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/main_recyclerview"
android:layout_width="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingBottom="8dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="#+id/main_chips"
android:layout_marginBottom="8dp"
**bindAdapter:attachFloatingButton="#{mainFb}"**
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0.0"/>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="#+id/main_fb"
android:layout_width="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
style="#style/Widget.Design.FloatingActionButton"
app:layout_constraintEnd_toEndOf="parent"
android:layout_height="wrap_content"
android:layout_margin="18dp"
android:background="#color/colorPrimaryDark"
app:icon="#drawable/ic_add_black_24dp"/>
According to me the best way to implement this would be as below.
public class ScrollingFABBehavior extends FloatingActionButton.Behavior {
private static final String TAG = "ScrollingFABBehavior";
public ScrollingFABBehavior(Context context, AttributeSet attrs) {
super();
// Log.e(TAG, "ScrollAwareFABBehavior");
}
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);
//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();
}
}}
For detailed answer check this out. Hide FloatingActionButton on scroll of RecyclerView
for Kotlin it is very simple (API 23+)
myRecyclerView.setOnScrollChangeListener { _, _, _, _, oldScrollY ->
if (oldScrollY < 0) myFAB.hide() else myFAB.show()
}
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();
}
}});
Just to add, for NestedScrollView the approach will be something like the following:
// register the extended floating action Button
final ExtendedFloatingActionButton extendedFloatingActionButton = findViewById(R.id.extFloatingActionButton);
// register the nestedScrollView from the main layout
NestedScrollView nestedScrollView = findViewById(R.id.nestedScrollView);
// handle the nestedScrollView behaviour with OnScrollChangeListener
// to extend or shrink the Extended Floating Action Button
nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
#Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
// the delay of the extension of the FAB is set for 12 items
if (scrollY > oldScrollY + 12 && extendedFloatingActionButton.isExtended()) {
extendedFloatingActionButton.shrink();
}
// the delay of the extension of the FAB is set for 12 items
if (scrollY < oldScrollY - 12 && !extendedFloatingActionButton.isExtended()) {
extendedFloatingActionButton.extend();
}
// if the nestedScrollView is at the first item of the list then the
// extended floating action should be in extended state
if (scrollY == 0) {
extendedFloatingActionButton.extend();
}
}
});
I've taken this code from GeeksForGeeks

CoordinatorLayout: Hiding/Showing half visible toolbar?

Id like to achieve a similar effect as the one you can see in Google Play store, where by scrolling the content the Toolbar goes off-screen as you scroll.
This works fine with the CoordinatorLayout (1) introduced at #io15, however: If you stop the scroll "mid-way" the Toolbar remains on screen, but is cut in half: I want it to animate off-screen, just like in the Google Play store. How can I achieve that?
Now the Android Support Library 23.1.0 has a new scroll flag SCROLL_FLAG_SNAP which allows you to achieve this effect.
AppBarLayout supports a number of scroll flags which affect how children views react to scrolling (e.g. scrolling off the screen). New to this release is SCROLL_FLAG_SNAP, ensuring that when scrolling ends, the view is not left partially visible. Instead, it will be scrolled to its nearest edge, making fully visible or scrolled completely off the screen.
Activity Layout file :
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize"
android:clipToPadding="false"/>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>
</FrameLayout>
Now inside the activity, setup Toolbar and RecyclerView. Assign OnScrollListener to RecyclerView
recyclerView.setOnScrollListener(new MyScrollListener(this));
Extend MyScrollListerner from RecyclerView.OnScrollListener.
public abstract class MyScrollListener extends RecyclerView.OnScrollListener {
private static final float TOOLBAR_HIDE_THRESHOLD = 10;
private static final float TOOLBAR_SHOW_THRESHOLD = 70;
private int mToolbarOffset = 0;
private boolean mControlsVisible = true;
private int mToolbarHeight;
private int mTotalScrolledDistance;
public MyScrollListener(Context context) {
final TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
new int[]{R.attr.actionBarSize});
mToolbarHeight = (int) styledAttributes.getDimension(0, 0);
styledAttributes.recycle();
return toolbarHeight;
mToolbarHeight = Utils.getToolbarHeight(context);
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(newState == RecyclerView.SCROLL_STATE_IDLE) {
if(mTotalScrolledDistance < mToolbarHeight) {
setVisible();
} else {
if (mControlsVisible) {
if (mToolbarOffset > TOOLBAR_HIDE_THRESHOLD) {
setInvisible();
} else {
setVisible();
}
} else {
if ((mToolbarHeight - mToolbarOffset) > TOOLBAR_SHOW_THRESHOLD) {
setVisible();
} else {
setInvisible();
}
}
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
clipToolbarOffset();
onMoved(mToolbarOffset);
if((mToolbarOffset <mToolbarHeight && dy>0) || (mToolbarOffset >0 && dy<0)) {
mToolbarOffset += dy;
}
if (mTotalScrolledDistance < 0) {
mTotalScrolledDistance = 0;
} else {
mTotalScrolledDistance += dy;
}
}
private void clipToolbarOffset() {
if(mToolbarOffset > mToolbarHeight) {
mToolbarOffset = mToolbarHeight;
} else if(mToolbarOffset < 0) {
mToolbarOffset = 0;
}
}
private void setVisible() {
if(mToolbarOffset > 0) {
onShow();
mToolbarOffset = 0;
}
mControlsVisible = true;
}
private void setInvisible() {
if(mToolbarOffset < mToolbarHeight) {
onHide();
mToolbarOffset = mToolbarHeight;
}
mControlsVisible = false;
}
public abstract void onMoved(int distance);
public abstract void onShow();
public abstract void onHide();
}
Overriding the AppBarLayout seems to be a better solution, as there are two possible scroll-events - of the entire CoordinatorLayout, and of the RecyclerView/NestedScrollView
See this answer as a possible working code:
https://stackoverflow.com/a/32110089/819355

Categories

Resources