-
-
Save NikolaDespotoski/a201165e1a3f0b6cc57852559843b0dc to your computer and use it in GitHub Desktop.
import android.content.Context; | |
import android.os.Parcelable; | |
import android.support.annotation.IntDef; | |
import android.support.design.widget.CoordinatorLayout; | |
import android.support.v4.view.ViewCompat; | |
import android.support.v4.view.WindowInsetsCompat; | |
import android.util.AttributeSet; | |
import android.view.View; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
/** | |
* Created by Nikola on 11/22/2015. | |
*/ | |
public abstract class VerticalScrollingBehavior<V extends View> extends CoordinatorLayout.Behavior<V> { | |
private int mTotalDyUnconsumed = 0; | |
private int mTotalDy = 0; | |
@ScrollDirection | |
private int mOverScrollDirection = ScrollDirection.SCROLL_NONE; | |
@ScrollDirection | |
private int mScrollDirection = ScrollDirection.SCROLL_NONE; | |
public VerticalScrollingBehavior(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
} | |
public VerticalScrollingBehavior() { | |
super(); | |
} | |
@Retention(RetentionPolicy.SOURCE) | |
@IntDef({ScrollDirection.SCROLL_DIRECTION_UP, ScrollDirection.SCROLL_DIRECTION_DOWN}) | |
public @interface ScrollDirection { | |
int SCROLL_DIRECTION_UP = 1; | |
int SCROLL_DIRECTION_DOWN = -1; | |
int SCROLL_NONE = 0; | |
} | |
/* | |
@return Overscroll direction: SCROLL_DIRECTION_UP, CROLL_DIRECTION_DOWN, SCROLL_NONE | |
*/ | |
@ScrollDirection | |
public int getOverScrollDirection() { | |
return mOverScrollDirection; | |
} | |
/** | |
* @return Scroll direction: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN, SCROLL_NONE | |
*/ | |
@ScrollDirection | |
public int getScrollDirection() { | |
return mScrollDirection; | |
} | |
/** | |
* @param coordinatorLayout CoordinatoyLayout parent | |
* @param child View child | |
* @param direction Direction of the overscroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN | |
* @param currentOverScroll Unconsumed value, negative or positive based on the direction; | |
* @param totalOverScroll Cumulative value for current direction | |
*/ | |
public abstract void onNestedVerticalOverScroll(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int direction, int currentOverScroll, int totalOverScroll); | |
/** | |
* @param scrollDirection Direction of the overscroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN | |
*/ | |
public abstract void onDirectionNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection); | |
@Override | |
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { | |
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; | |
} | |
@Override | |
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { | |
super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); | |
} | |
@Override | |
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { | |
super.onStopNestedScroll(coordinatorLayout, child, target); | |
} | |
@Override | |
public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { | |
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); | |
if (dyUnconsumed > 0 && mTotalDyUnconsumed < 0) { | |
mTotalDyUnconsumed = 0; | |
mOverScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP; | |
} else if (dyUnconsumed < 0 && mTotalDyUnconsumed > 0) { | |
mTotalDyUnconsumed = 0; | |
mOverScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN; | |
} | |
mTotalDyUnconsumed += dyUnconsumed; | |
onNestedVerticalOverScroll(coordinatorLayout, child, mOverScrollDirection, dyConsumed, mTotalDyUnconsumed); | |
} | |
@Override | |
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) { | |
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed); | |
if (dy > 0 && mTotalDy < 0) { | |
mTotalDy = 0; | |
mScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP; | |
} else if (dy < 0 && mTotalDy > 0) { | |
mTotalDy = 0; | |
mScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN; | |
} | |
mTotalDy += dy; | |
onDirectionNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, mScrollDirection); | |
} | |
@Override | |
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, boolean consumed) { | |
super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); | |
mScrollDirection = velocityY > 0 ? ScrollDirection.SCROLL_DIRECTION_UP : ScrollDirection.SCROLL_DIRECTION_DOWN; | |
return onNestedDirectionFling(coordinatorLayout, child, target, velocityX, velocityY, mScrollDirection); | |
} | |
protected abstract boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, @ScrollDirection int scrollDirection); | |
@Override | |
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) { | |
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); | |
} | |
@Override | |
public WindowInsetsCompat onApplyWindowInsets(CoordinatorLayout coordinatorLayout, V child, WindowInsetsCompat insets) { | |
return super.onApplyWindowInsets(coordinatorLayout, child, insets); | |
} | |
@Override | |
public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) { | |
return super.onSaveInstanceState(parent, child); | |
} | |
} |
My only issue with the SnackbarLayout is displayed in front of the BottomNavigationView, I add the navigation view dynamically to the CoordinatorLayout, I checked with uiautomatorviewer and the snackbar layout is indeed added before the bottom bar.
Sorry, previously I stated that the snack bar added before the bottom bar, but in the contrary, the snackbar added after the bottom bar, so it covers the bottom bar fully, but the padding of the snack bar set correctly. See the
screenshot.
Ok, It seems the BottomNavigationBehavior cannot find the tab layout, so the elevation is not set.
@laszlo-galosi Sorry for delayed response. Yes, Behavior MUST find the bottomnavigationview so it can set 8dp or do it in your xml.
Cheers,
Hi Nikola, thanks for the great work, I wonder how can we expand the bottom bar manually with some method similar to appbar.setExpanded(boolean)?
[Updated]
I just wrote the method below based on your code:
private void expandBottomBar(View bottomBar){
if (ViewUnit.getWindowHeight(context) < (int) bottomBar.getY() + bottomBar.getHeight()){
ViewCompat.animate(bottomBar).setDuration(100)
.setInterpolator(BottomNavigationBehavior.INTERPOLATOR)
.translationY(0).start();
}
}
@ghost There is a method setHidden(V view, boolean bottomLayoutHidden)
it can be accessed:
BottomNavigationBehavior behavior = BottomNavigationBehavior.from(mBottomNavigationView);
if(behavior != null){
behavior.setHidden(mBottomNavigationView, true); //or false
}
I believe animateOffset should be called with +view.getHeight() when bottomLayoutHidden == true
as per te following fix
public void setHidden(V view, boolean bottomLayoutHidden) {
if (!bottomLayoutHidden && hidden) {
animateOffset(view, 0);
} else if (bottomLayoutHidden && !hidden) {
animateOffset(view, view.getHeight());
}
hidden = bottomLayoutHidden;
}
@eeVoskos Thanks for the remark, yes, the default duration is 300ms. I wondered if 300ms was slow, just from looking at the animation videos in the link you provided, I decided to put it 100. :)
I will get rid of that line and update the gist.
Cheers.