Adding more than one behavior to a FloatingActionButton - android

duiring my last project I discovered the Android Material Design Library. It is pretty mighty and I had fun working with it. I added a custom behavior to my FloatingActionButton, so it disappears while scrolling downwards. Now I mentioned, if a SnackBar is shown the position of the FAB isn't handled automatically anymore.
After some debugging I found out, that setting the anchor to the recyclerView and adding the customBehaviour for scrolling the default behavior from the CoordinatorLayout depending the SnackBar is gone.
So I ask myself, can I add more then one Behaviour to my FAB? Or can I somehow tell it, that the defualt one should not be overwritten, but extended?
Or can I write more than one of those?
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
return dependency instanceof RecyclerView;
}

Okay, I found a method to realise the wished behavior, but it has more of an workaround than an answer.
If I add the Scrollbehavior programmatically in the Java Code like this:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
sendMailFAB.show();
}
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0 && sendMailFAB.isShown())
sendMailFAB.hide();
}
});
And then delete the custom behavior and it's anchor in the .xml file, the CoordinatorLayout's default behavior handles the Snackbar and the onScrollListener the scroll behavior.
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment_coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="de.flowment.designExample.StartActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/startRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
app:layout_anchorGravity="bottom|end"
android:src="#android:drawable/ic_dialog_email" />
<!-- DELETE THIS PART, BECAUSE IT'S NOT USED ANYMORE AND BLOCKS THE DEFAULT.
app:layout_anchor="#id/startRecyclerView"
app:layout_behavior="de.flowment.designExample.FABScrollBehavior" />-->
</android.support.design.widget.CoordinatorLayout>
So I achived two behavior, but like I said this is more of an workaround.

Related

Transparent Toolbar over RecyclerView is not hidding when scrolling

I've some troubles trying to hide toolbar when user scrolls over the recyclerView.
The toolbar is transparent and is over the recyclerView (through FrameLayout). I've searched a lot but I haven't found any solution to solve this incorrect behaviour.
Currently, I've this xml:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:statusBarBackground="#android:color/transparent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:fitsSystemWindows="true">
<include layout="#layout/toolbar_activity" />
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</FrameLayout>
</android.support.design.widget.CoordinatorLayout>
With this code, the toolbar is fixed at the top and it's not affected by app:layout_behavior="#string/appbar_scrolling_view_behavior". I've tried moving that attribute to the FrameLayout but in this case, the recyclerview is below the toolbar, not behind it.
Any idea of how can I solve this? I'm going crazy...
set property
app:layout_scrollFlags="scroll|enterAlways"
to child view of android.support.design.widget.AppBarLayout
Create a custom class and then extend RecyclerView.OnScrollListener
public class ScrollListener extends RecyclerView.OnScrollListener {
public ScrollListener() {
}
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
System.out.println("The RecyclerView is not scrolling");
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
System.out.println("Scrolling now");
break;
case RecyclerView.SCROLL_STATE_SETTLING:
System.out.println("Scroll Settling");
break;
}
}
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) {
//scrolling downwards: hide/show toolbar
System.out.println("Scrolled Downwards");
} else if (dy < 0) {
//scrolling downwards: hide/show toolbar
}
}
}
Attach listener to recycler view
mRecyclerView.addOnScrollListener(new ScrollListener());
Add these scroll flags to child of your app bar layout
app:layout_scrollFlags="scroll|enterAlways|snap"

Show/hide BottomNavigationView on scroll in CoordinatorLayout with AppBarLayout

I am trying to use both AppBarLayout and BottomNavigationLayout in a single CoordinatorLayout and I'm having difficulties hiding the BottomNavigationLayout as required by the material guideline.
I mean something like this:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false">
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_insetEdge="top"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="#style/AppTheme.PopupOverlay"
app:layout_scrollFlags="scroll|enterAlways"/>
</android.support.design.widget.AppBarLayout>
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_nav"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
app:menu="#menu/menu_bottom_navigation"/>
<FrameLayout
android:id="#+id/content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
As you can see, I also have a FrameLayout that's used to contain a fragment with the actual content. Currently there are no default/built-in behaviors for the BottomNavigationView - neither for the view itself, nor for its siblings. The existing appbar_scrolling_view_behavior handles the content view in coordination with the appbar but ignores other siblings.
I am looking for a solution to hide and show both the appbar and the bottom navigation view on scroll.
After a day or two of searching I settled with a custom Behavior attached to the BottomNavigationView. Its main idea is to detect when the BottomNavigationView's sibling is scrolled so that it can hide the BottomNavigationView. Something like this:
public class BottomNavigationBehavior extends CoordinatorLayout.Behavior<BottomNavigationView> {
public BottomNavigationBehavior() {
super();
}
public BottomNavigationBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, BottomNavigationView child, View dependency) {
boolean dependsOn = dependency instanceof FrameLayout;
return dependsOn;
}
#Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, BottomNavigationView child, View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
#Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, BottomNavigationView child, View target, int dx, int dy, int[] consumed) {
if(dy < 0) {
showBottomNavigationView(child);
}
else if(dy > 0) {
hideBottomNavigationView(child);
}
}
private void hideBottomNavigationView(BottomNavigationView view) {
view.animate().translationY(view.getHeight());
}
private void showBottomNavigationView(BottomNavigationView view) {
view.animate().translationY(0);
}
}
As you can see, I'm using simple ViewPropertyAnimator, obtained using the child views's animate method. This leads to a simple animation that doesn't really match the AppBarLayout's behavior but it's decent enough to look good and at the same time it's simple enough to implement.
I expect that at some point the Android team will add a default Behavior for the BottomNavigationView in the support library so I don't think it's reasonable to invest a lot more time to exactly duplicate the AppBarLayout's behavior.
edit (April 2018): see the comments section for a minor clarification about onStartNestedScroll and onNestedPreScroll and their new versions.
You can also use HideBottomViewOnScrollBehavior. This behavior works in basically the same way, but also handles cancelling any existing animations that are running which should be better for performance.

Floating Action Button not visible on application start

I have a fragment with a RecyclerView inside of a ViewPager, and I want to manage the behavior of a Floating Action Button located in my MainActivity. In my onCreateView() of the Fragment, I have the following code:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_recycler, container, false);
recycler = (RecyclerView) v.findViewById(R.id.recycler);
initiate_recycler();
if (getActivity() instanceof MainActivity) {
((MainActivity) getActivity()).set_fab_behavior(recycler);
}
return v;
}
In the MainActivity, I have the set_fab_behavior() method, which is:
public void set_fab_behavior(RecyclerView recyclerView) {
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy < 0) {
fab.show();
} else {
fab.hide();
}
}
});
}
The code works fine, and it shows/hides the Floating Action Button when it is scrolled. However, the fab isn't initialized when the app first runs. It isn't there, and when the RecyclerView is scrolled for the first time, it doesn't animate in. Instead, it just appears. How should I modify the code so the fab is present on the application start?
Also, note that the fab works fine when this method is not called, but it doesn't hide on scroll, which is necessary. I already tried passing the fab to the fragment via a getter method, and adding the OnScrollListener in the fragment, but the results were the same.
Edit: below is the MainActivity layout file
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.gworkman.shoutout.MainActivity">
<android.support.design.widget.FloatingActionButton
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="bottom"
android:background="#color/white"
app:backgroundTint="#color/white"
android:src="#drawable/ic_mic"
app:elevation="12dp"
android:id="#+id/fab"/>
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/pager"/>
</android.support.design.widget.CoordinatorLayout>

Make FAB respond to Soft Keyboard show/hide changes

I've seen various posts about FABs responding to Snackbar popups at the bottom of the screen as well as scroll-sensitive FABs. But is there some implementation of FloatingActionButton.Behavior (or similar) to move the FAB above the keyboard when it pops up?
Right now, the keyboard covers the FAB when I click for example in an EditText box. My goal is to animate the FAB so it is always visible, independent of the keyboard status.
EDIT: Both android:windowSoftInputMode="adjustResize" and ...="adjustPan" won't change anything. Well, adjustResize resizes the underlying layout (which is in my case a map) but the FAB doesn't move.
Hi there i know it's old but for future or current readers/searchers and also the thread maker, he hasn't found answer yet. This is how i am having this behaviour in my app.
Fab hides on RecyclerView scroll, goes up when snack bar pops out, if fab is not shown and snackbar popus up and if you scroll then still Fab will be shown top of snack bar and will move down when SB disappears and last with keypad if it opens Fab will be pushed up. (sorry, i had to write coz i don't know how to give gif with eclipse emulator)
Image
Layout
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layout_drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/GrayGood"
android:clipToPadding="false"
android:fitsSystemWindows="true" >
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layout_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.AppBarLayout
android:id="#+id/layout_appLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark" >
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap"
android:background="?attr/colorPrimary" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerViewMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:clipToPadding="false" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/floating_action_button_margin"
app:layout_behavior="com.faisal.cvcd.anim.ScrollingFABAnimation"
android:src="#drawable/ic_fab"
android:tint="#color/White"
app:backgroundTint="#color/colorPrimary"
app:borderWidth="0dp"
app:elevation="6dp"
app:fabSize="normal" />
</android.support.design.widget.CoordinatorLayout>
</android.support.v4.widget.DrawerLayout>
As you can see i am using FabAnimation class to override some of its default methods.
ScrollingFABAnimation
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
public class ScrollingFABAnimation extends
CoordinatorLayout.Behavior<FloatingActionButton> {
public ScrollingFABAnimation(Context context, AttributeSet attrs) {
super(context, attrs);
}
//For SnackBar to push up the Floating Button when it pops up
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
return dependency instanceof Snackbar.SnackbarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
//For FAB to hide or show on scroll
#Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target,
int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child,
directTargetChild, target, nestedScrollAxes);
}
#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.VISIBLE) {
child.show();
}
}
}
I had the same problem, i tried to add a ScrollView inside my root view but it did not works because the content doesn't exceed the display's height (the fab react as expected in that case).
So i tried android:windowSoftInputMode="adjustResize" and it works for me.
For informations here's what my layout xml looks like:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<ManyViewsExceptScrollViews/>
<android.support.design.widget.FloatingActionButton
[..]
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
it's a Fragment inflated in an activity (in a FrameLayout that match_parent the root view) for which i added android:windowSoftInputMode="adjustResize" in the manifest.

Hiding Toolbar smoothly on RecyclerView Scroll?

Currently I've a RecyclerView that holds some list of items. I'm listening the Scroll listener of RecyclerView and if the RecyclerView at some point say 500, it should hide the toolbar and it should remain hide when it crosses to 500+. Similarly, it shows the toolbar when i reaches <= 450.
This is the code I've tried so far. The problem is,
It hides the toolbar but it flashes when it hides or shows at that mentioned point.
How to achieve a smooth toolbar hide?
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
scrollD = scrollD + dy;
Log.d("key", "DY is .." + (dy + scrollD));
if (scrollD >= 500) {
// code to hide
}
if (scrollD <= 450) {
// code to show
}
}
});
Use CoordinatorLayout instead of Linear/Relative layout and add the following attribute to the toolbar.
app:layout_scrollFlags="scroll|enterAlways"
CoordinatorLayout handles visibility of toolbar by hiding it when the user scrolls down and showing it again when the user scrolls up.
Code:
<?xml version="1.0" encoding="utf-8"?>
<!-- $Id$ -->
<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.support.design.widget.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
Refer this link : https://mzgreen.github.io/2015/06/23/How-to-hideshow-Toolbar-when-list-is-scrolling(part3)/
I was also searching for the same solution and found this.
Working fine with me.
TO hide a Toolbar
mToolbar.animate().translationY(-mToolbar.getBottom()).setInterpolator(new AccelerateInterpolator()).start();
To Show the Toolbar:
mToolbar.animate().translationY(mToolbar.getTop()).setInterpolator(new AccelerateInterpolator()).start();
call those lines on recycler view's scroll listener.
Now since listener gives you dx and dy values of Toolbar.
So in above lines of code, instead of mToolbar.getTop() You can write:
int heightDelta += dy;
bothToolbarLayouts.animate().translationY(-heightDelta).setInterpolator(new AccelerateInterpolator()).start();
Voila you are done!
Alternatively to understand it more better follow this link

Categories

Resources