Rolling FloatingActionButton Behavior

I have a sort of habit browsing materialup.com for specific keywords from Material guidelines or newest posts. Few days ago I came across this concept:

A rolling fab, freaking lolz.

Rolling in

Idea behind this is building the rolling behavior on top of the existing FloatinghActionButton.Behavior to preserve the SnackBar behavior.

We need to roll the FAB only when scrolling for all the views that implement nested scrolling:

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

@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, ..., int nestedScrollAxes) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
  1. Start rolling out the FAB when we have scrolled around its height.
  2. Apply tension threshold of 0.5f, upon which we are going to start rolling it out completely.
  3. Roll in the FAB when we start scrolling down.
@Override
public void onNestedPreScroll(... FloatingActionButton child... int dx, int dy, ..) {
if (dy > 0 && mTotalDy < 0) {
mTotalDy = 0;
if (mTensionFactor <= 0.5f)
rollInFabCompletely(child);
} else if (dy < 0 && mTotalDy > 0) {
mTotalDy = 0;
}

mTotalDy += dy;
if (mTotalDy >= child.getHeight() && getRollingFabState() == IDLE) {
float rollBy = (float) (mTotalDy - child.getHeight()) / child.getHeight();
rollOutFabBy(child, rollBy);
} else if (mTotalDy < -child.getHeight()) {
if (getRollingFabState() == RollingFabState.ROLLED_OUT) {
rollInFabCompletely(child);
} else if (getRollingFabState() == RollingFabState.ROLLING_OUT) {
ViewCompat.animate(child).cancel();
rollInFabCompletely(child);
}
}
}

Tension will be used to restore original FAB position and rotation if we haven’t scrolled enough.

@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target) {
if (mTensionFactor >= 0.5)
postRollFabOutCompletely(child);
else if (mTensionFactor < 0.5 && getRollingFabState() != RollingFabState.ROLLED_OUT)
postRollFabInCompletely(child);
}

Code: