Android -FAB Behaviour with half list - android

I have the FAB working when recyclerview has enough items to scroll, but i need to handle the case when recyclerview does not scroll (the total of items do not cover the screen).
At the moment this is how I handle the scroll:
public class FABBehavior extends FloatingActionButton.Behavior {
public FABBehavior() {
super();
}
public FABBehavior(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton 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) {
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) {
child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
}
}
#Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child, final View directTargetChild, final View target, final int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
}
How to handle when recyclerview has few items?

You have to handle another case independently from CoordinatorLayout.
override a function layoutDependsOn:
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
return super.layoutDependsOn(parent, child, dependency) || dependency instanceof RecyclerView;
}
onNestedScroll should also handle another case:
if (target instanceof RecyclerView) {
handleRecyclerViewScrolling(target, child);
return;
}
handleRecyclerViewScrolling should look like:
private void handleRecyclerViewScrolling(View target, FloatingActionButton child) {
if (scrollListener != null) {
return;
}
RecyclerView recyclerView = (RecyclerView) target;
scrollListener = new RecyclerViewScrollListener(child);
recyclerView.addOnScrollListener(scrollListener);
}
scrollListener should be a field in your FABBehavior class. Also declare inner class inside FABBehavior:
private class RecyclerViewScrollListener extends RecyclerView.OnScrollListener {
FloatingActionButton mChild;
public RecyclerViewScrollListener(FloatingActionButton child) {
this.mChild = child;
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
mChild.show();
} else {
mChild.hide();
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (!recyclerView.canScrollVertically(Integer.MAX_VALUE)) {
mChild.show();
}
}
}
RecyclerViewScrollListener hides FAB, when it is scrolling and shows it when it is in idle state.

Related

How to scroll up BottomNavigationBar with last item in Recycler view

I'm trying to make scrolling Bottom navigation bar view, which will scroll down when scrolling down, scroll up, when scrolling up, and scroll up with last item of recycler view when scrolling to the end. But I don't know how to implement the last thing. This is my BottomNavigationBehavior class:
public class NavigationBehavior extends CoordinatorLayout.Behavior {
private float height = 0;
public final void BottomNavigationBehavior() {
}
public NavigationBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onLayoutChild(#NonNull #NotNull CoordinatorLayout parent, #NonNull #NotNull View child, int layoutDirection) {
ViewGroup.MarginLayoutParams paramsCompat =
(ViewGroup.MarginLayoutParams) child.getLayoutParams();
height = child.getMeasuredHeight() + paramsCompat.bottomMargin;
return super.onLayoutChild(parent, child, layoutDirection);
}
#Override
public boolean onStartNestedScroll(#NonNull #NotNull CoordinatorLayout coordinatorLayout, #NonNull #NotNull View child, #NonNull #NotNull View directTargetChild, #NonNull #NotNull View target, int axes, int type) {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
#Override
public void onNestedPreScroll(#NonNull #NotNull CoordinatorLayout coordinatorLayout, #NonNull #NotNull View child, #NonNull #NotNull View target, int dx, int dy, #NonNull #NotNull int[] consumed, int type) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
child.setTranslationY(Float.max(0.0F, Float.min(height, child.getTranslationY() + (float)dy)));
}
public void setEndOfList(boolean endOfList) {
this.endOfList = endOfList;
}
}
Can someone help me ?

onClick method inside RecyclerView not working after NestedScrollView scrolled

I checked this stackoverflow question cause it's very similar, but the Google's bug have been fixed in current versions, but I still having the problem.
I have an RecyclerView inside a NestedScrollView, after NestedScrollView scrolled if I click on item inside RecyclerView, onClick method does not work propertly.
Can anyone help me? Thanks
Okey, I found the solution here, we need:
public class FixAppBarLayoutBehavior extends AppBarLayout.Behavior {
public FixAppBarLayoutBehavior() {
super();
}
public FixAppBarLayoutBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target,
int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, type);
stopNestedScrollIfNeeded(dyUnconsumed, child, target, type);
}
#Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target, int dx, int dy, int[] consumed, int type) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
stopNestedScrollIfNeeded(dy, child, target, type);
}
private void stopNestedScrollIfNeeded(int dy, AppBarLayout child, View target, int type) {
if (type == ViewCompat.TYPE_NON_TOUCH) {
final int currOffset = getTopAndBottomOffset();
if ((dy < 0 && currOffset == 0)
|| (dy > 0 && currOffset == -child.getTotalScrollRange())) {
ViewCompat.stopNestedScroll(target, ViewCompat.TYPE_NON_TOUCH);
}
}
}
}
and, in our AppBarLayout:
<android.support.design.widget.AppBarLayout>
...
app:layout_behavior="your.package.FixAppBarLayoutBehavior"
...
</android.support.design.widget.AppBarLayout>
Your RecyclerView shouldn't permit nested scrolling, so it must have nestedScrollingEnabled="false"
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"/>

Vertical Scrolling CoordinatorLayout Behavior for BottomNavigationView with RecyclerView vertical scrolling

How to define a CoordinatorLayout.Behavior class for BottomNavigationView which scrolls synchronized with the RecyclerView veritical scroll.
I have seen this and this, but all it does is just show/hide the NavigationView on veritical scroll events immediately. I don't want to show/hide the NavigationView immediately, instead i want a behavior something like the AppbarLayout having a Toolbar with scroll flag as app:layout_scrollFlags="scroll|enterAlways".
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);
}
}
After some trying I came up with this solution:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(dy > 0 && visible){
mBinding.bnv.test.setY(mBinding.bnv.getY() + dy);
DisplayMetrics metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
visible = mBiding.bnv.getY() > metrics.heightPixels;
if(!visible) {
mBinding.bnv.setY(metrics.heightPixels);
}
} else {
mBinding.bnv.setY(mBinding.bnv.getY() + dy);
DisplayMetrics metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
visible = mBinding.bnv.getY() > metrics.heightPixels;
}
}
So you are scrollling the BottomNavigationView with the recycler view
Or with CoordinatorLayout.Behavior class:
public class ViewScrollWithRecyclerViewBehavior extends CoordinatorLayout.Behavior<View> {
private boolean visible = true;
private boolean inStartPosition = true;
private float oldY;
private DisplayMetrics metrics;
public ViewScrollWithRecyclerViewBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
metrics = Resources.getSystem().getDisplayMetrics();
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, View fab, View dependency) {
return dependency instanceof AppBarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
if (dependency instanceof AppBarLayout) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
float dy = oldY - dependency.getY();
if(dy > 0 && visible){
moveDown(child, oldY);
} else if(!inStartPosition) {
moveUp(child, oldY);
}
oldY = dependency.getY();
}
return true;
}
private void moveUp(View child, float dy){
if(child.getY() + dy >= metrics.heightPixels - child.getHeight()){
child.setY(metrics.heightPixels - child.getHeight());
} else {
child.setY(child.getY() + dy);
}
inStartPosition = child.getY() == metrics.heightPixels - child.getHeight();
visible = child.getY() > metrics.heightPixels;
}
private void moveDown(View child, float dy){
child.setY(child.getY() + dy);
visible = child.getY() > metrics.heightPixels;
if(!visible) {
child.setY(metrics.heightPixels);
}
}
#Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final View child,
final View directTargetChild, final View target, final int nestedScrollAxes) {
return true;
}
#Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout,
final View child,
final View target, final int dxConsumed, final int dy,
final int dxUnconsumed, final int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dy, dxUnconsumed, dyUnconsumed);
if(dy > 0 && visible){
moveDown(child, dy);
} else if(!inStartPosition) {
moveUp(child, dy);
}
}
}

onNestedScroll called only once

I have a class
public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super();
}
public ScrollAwareFABBehavior() {
super();
}
#Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,final View directTargetChild, final View target, final int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
#Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton 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 && child.getVisibility() == View.VISIBLE) {
child.hide();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
child.show();
}
}
}
the problem is that onNestedScroll is called only once when I scroll up a recyclerview, so the fab is hiding and never shows again. Here is layout I am using
<?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:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/refresh_layout"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
app:layoutManager="#string/vertical_linear_layout_manager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/add"
android:layout_width="wrap_content"
app:layout_behavior="mypackagename.util.ScrollAwareFABBehavior"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
app:useCompatPadding="true"
android:layout_margin="16sp"
android:src="#drawable/ic_add"/>
</android.support.design.widget.CoordinatorLayout>
My support libraries version is 25.1.0
I solved the problem changing the visibility from GONE to INVISIBLE with the following code.
#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(new FloatingActionButton.OnVisibilityChangedListener() {
#Override
public void onShown(FloatingActionButton fab) {
super.onShown(fab);
}
#Override
public void onHidden(FloatingActionButton fab) {
super.onHidden(fab);
fab.setVisibility(View.INVISIBLE);
}
});
} else if (dyConsumed <= 0 && child.getVisibility() != View.VISIBLE) {
child.show();
}
}
I have just answered
to absolutely the same problem in another post, check it.
Speaking shortly, use the following:
compile 'com.android.support:design:25.0.1'
#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 && child.getVisibility() == View.VISIBLE) {
child.hide(new FloatingActionButton.OnVisibilityChangedListener() {
#Override
public void onHidden(FloatingActionButton fab) {
super.onHidden(fab);
fab.setVisibility(View.INVISIBLE);
}
});
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
child.show();
}
}
Do not set the visibility of FAB to GONE in child.hide() - use INVISIBLE instead. From 25.1.0, the scroll events are not passed on to views whose visibility is GONE
For me i replace child.hide() with ((View)child).setVisibility(View.INVISIBLE).

How to hide FloatingActionMenu inside CoordinatorLayout on Scroll?

Im using this great library to create a FloatingActionMenu. I use it inside a coordinator layout:
<android.support.design.widget.CoordinatorLayout
android:id="#+id/evaluations_list_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/evaluations_list_textview_time"
android:layout_below="#+id/evaluations_list_textview_name"
android:importantForAccessibility="no">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/evaluations_list_swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no">
<android.support.v7.widget.RecyclerView
android:id="#+id/evaluations_list_row_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:importantForAccessibility="no"
android:padding="10dp"
android:textColor="#color/textcolorprimary" />
</android.support.v4.widget.SwipeRefreshLayout>
<com.github.clans.fab.FloatingActionMenu
android:id="#+id/evaluations_list_floating_action_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:scaleType="fitXY"
android:src="#drawable/vector_drawable_ic_add_white"
app:layout_anchor="#id/evaluations_list_row_parent"
app:layout_anchorGravity="bottom|right|end"
app:layout_behavior="my.project.views.buttons.ScrollFAMBehaviour"
app:menu_animationDelayPerItem="50"
app:menu_colorNormal="#color/primaryColorDark"
app:menu_colorPressed="#color/accentColor"
app:menu_fab_size="normal"
app:menu_openDirection="up">
<com.github.clans.fab.FloatingActionButton
android:id="#+id/evaluations_list_floating_action_button_filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:scaleType="fitXY"
android:src="#drawable/vector_drawable_ic_check_white"
app:fab_colorNormal="#color/primaryColorDark"
app:fab_colorPressed="#color/accentColor"
app:fab_label="#string/fab_filter"
app:fab_size="mini" />
<com.github.clans.fab.FloatingActionButton
android:id="#+id/evaluations_list_floating_action_button_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:scaleType="fitXY"
android:src="#drawable/vector_drawable_ic_search_white"
app:fab_colorNormal="#color/primaryColorDark"
app:fab_colorPressed="#color/accentColor"
app:fab_label="#string/fab_search"
app:fab_size="mini" />
</com.github.clans.fab.FloatingActionMenu>
</android.support.design.widget.CoordinatorLayout>
Now i want to create a CoordinatorLayout.Behavior to hide the complete menu when the user is scrolling down and i want it to show up again when the user is scrolling up.
Bevor i used the FloatingActionMenu i used the classic FloatingActionButton. For that it was easily possible to achieve what i want using the following code:
public class ScrollFABBehaviour extends FloatingActionButton.Behavior {
private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
private boolean isAnimatingOut = false;
public ScrollFABBehaviour(Context context, AttributeSet attrs) {
super();
}
#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 && !this.isAnimatingOut && child.getVisibility() == View.VISIBLE) {
this.animateOut(child);
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
this.animateIn(child);
}
}
private void animateOut(final FloatingActionButton button) {
ViewCompat.animate(button).scaleX(0.0F).scaleY(0.0F).alpha(0.0F).setInterpolator(INTERPOLATOR).withLayer().setListener(new ViewPropertyAnimatorListener() {
public void onAnimationStart(View view) {
ScrollFABBehaviour.this.isAnimatingOut = true;
}
public void onAnimationCancel(View view) {
ScrollFABBehaviour.this.isAnimatingOut = false;
}
public void onAnimationEnd(View view) {
ScrollFABBehaviour.this.isAnimatingOut = false;
view.setVisibility(View.GONE);
}
}).start();
}
private void animateIn(FloatingActionButton button) {
button.setVisibility(View.VISIBLE);
ViewCompat.animate(button).scaleX(1.0F).scaleY(1.0F).alpha(1.0F).setInterpolator(INTERPOLATOR).withLayer().setListener(null).start();
}
}
Unfortunately i cant use this code on the FloatingActionMenu because it is not a FloatingActionButton and also does not inherit it. It is a custom ViewGroup.
How could i create such a behavior for the FloatingActionMenu?
This behavior is really obtained by creating a CoordinatorLayout.Behavior<View>. So instead of extending from FloatingActionButton.Behavior extend from CoordinatorLayout.Behavior<FloatingActionMenu> directly.
Here is the equivalent code that will work with the FloatingActionMenu.
public class ScrollFAMBehaviour extends CoordinatorLayout.Behavior<FloatingActionMenu>{
private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
private boolean isAnimatingOut = false;
public ScrollFAMBehaviour(Context context, AttributeSet attrs) {
super();
}
#Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionMenu 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, FloatingActionMenu child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 && !this.isAnimatingOut && child.getVisibility() == View.VISIBLE) {
this.animateOut(child);
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
this.animateIn(child);
}
}
private void animateOut(final FloatingActionMenu menu) {
ViewCompat.animate(menu).scaleX(0.0F).scaleY(0.0F).alpha(0.0F).setInterpolator(INTERPOLATOR).withLayer().setListener(new ViewPropertyAnimatorListener() {
public void onAnimationStart(View view) {
ScrollFAMBehaviour.this.isAnimatingOut = true;
}
public void onAnimationCancel(View view) {
ScrollFAMBehaviour.this.isAnimatingOut = false;
}
public void onAnimationEnd(View view) {
ScrollFAMBehaviour.this.isAnimatingOut = false;
view.setVisibility(View.GONE);
}
}).start();
}
private void animateIn(FloatingActionMenu menu) {
menu.setVisibility(View.VISIBLE);
ViewCompat.animate(menu).scaleX(1.0F).scaleY(1.0F).alpha(1.0F).setInterpolator(INTERPOLATOR).withLayer().setListener(null).start();
}
}

Categories

Resources