Permalink
Please sign in to comment.
Showing
with
324 additions
and 9 deletions.
- +2 −0 bottom-bar/bottom-bar.iml
- +1 −0 bottom-bar/build.gradle
- +74 −7 bottom-bar/src/main/java/com/roughike/bottombar/BottomBar.java
- +68 −0 bottom-bar/src/main/java/com/roughike/bottombar/scrollsweetness/BottomNavigationBehavior.java
- +148 −0 bottom-bar/src/main/java/com/roughike/bottombar/scrollsweetness/VerticalScrollingBehavior.java
- +1 −2 bottom-bar/src/main/res/layout/bb_bottom_bar_item_container.xml
- +30 −0 bottom-bar/src/main/res/layout/bb_bottom_bar_item_container_shy.xml
2
bottom-bar/bottom-bar.iml
1
bottom-bar/build.gradle
81
bottom-bar/src/main/java/com/roughike/bottombar/BottomBar.java
68
...om-bar/src/main/java/com/roughike/bottombar/scrollsweetness/BottomNavigationBehavior.java
| @@ -0,0 +1,68 @@ | ||
| +package com.roughike.bottombar.scrollsweetness; | ||
| + | ||
| +import android.support.design.widget.CoordinatorLayout; | ||
| +import android.support.v4.view.ViewCompat; | ||
| +import android.support.v4.view.ViewPropertyAnimatorCompat; | ||
| +import android.support.v4.view.animation.LinearOutSlowInInterpolator; | ||
| +import android.view.View; | ||
| +import android.view.animation.Interpolator; | ||
| + | ||
| +/** | ||
| + * Created by Nikola D. on 3/15/2016. | ||
| + * | ||
| + * Credit goes to Nikola Despotoski: | ||
| + * https://github.com/NikolaDespotoski | ||
| + */ | ||
| +public class BottomNavigationBehavior<V extends View> extends VerticalScrollingBehavior<V> { | ||
| + private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator(); | ||
| + private final int mBottomNavHeight; | ||
| + private final int mDefaultOffset; | ||
| + | ||
| + private ViewPropertyAnimatorCompat mTranslationAnimator; | ||
| + private boolean hidden = false; | ||
| + | ||
| + public BottomNavigationBehavior(int bottomNavHeight, int defaultOffset) { | ||
| + mBottomNavHeight = bottomNavHeight; | ||
| + mDefaultOffset = defaultOffset; | ||
| + } | ||
| + | ||
| + @Override | ||
| + public void onNestedVerticalOverScroll(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int direction, int currentOverScroll, int totalOverScroll) { | ||
| + } | ||
| + | ||
| + @Override | ||
| + public void onDirectionNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection) { | ||
| + handleDirection(child, scrollDirection); | ||
| + } | ||
| + | ||
| + private void handleDirection(V child, int scrollDirection) { | ||
| + if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_DOWN && hidden) { | ||
| + hidden = false; | ||
| + animateOffset(child, mDefaultOffset); | ||
| + } else if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_UP && !hidden) { | ||
| + hidden = true; | ||
| + animateOffset(child, mBottomNavHeight + mDefaultOffset); | ||
| + } | ||
| + } | ||
| + | ||
| + @Override | ||
| + protected boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, @ScrollDirection int scrollDirection) { | ||
| + handleDirection(child, scrollDirection); | ||
| + return true; | ||
| + } | ||
| + | ||
| + private void animateOffset(final V child, final int offset) { | ||
| + ensureOrCancelAnimator(child); | ||
| + mTranslationAnimator.translationY(offset).start(); | ||
| + } | ||
| + | ||
| + private void ensureOrCancelAnimator(V child) { | ||
| + if (mTranslationAnimator == null) { | ||
| + mTranslationAnimator = ViewCompat.animate(child); | ||
| + mTranslationAnimator.setDuration(300); | ||
| + mTranslationAnimator.setInterpolator(INTERPOLATOR); | ||
| + } else { | ||
| + mTranslationAnimator.cancel(); | ||
| + } | ||
| + } | ||
| +} |
148
...m-bar/src/main/java/com/roughike/bottombar/scrollsweetness/VerticalScrollingBehavior.java
| @@ -0,0 +1,148 @@ | ||
| +package com.roughike.bottombar.scrollsweetness; | ||
| + | ||
| +import android.content.Context; | ||
| +import android.os.Parcelable; | ||
| +import android.support.annotation.IntDef; | ||
| +import android.support.design.widget.CoordinatorLayout; | ||
| +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 D. on 11/22/2015. | ||
| + * | ||
| + * Credit goes to Nikola Despotoski: | ||
| + * https://github.com/NikolaDespotoski | ||
| + */ | ||
| +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 | ||
| + * @param 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 & View.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); | ||
| + } | ||
| + | ||
| +} |
3
bottom-bar/src/main/res/layout/bb_bottom_bar_item_container.xml
30
bottom-bar/src/main/res/layout/bb_bottom_bar_item_container_shy.xml
| @@ -0,0 +1,30 @@ | ||
| +<?xml version="1.0" encoding="utf-8"?> | ||
| +<FrameLayout | ||
| + xmlns:android="http://schemas.android.com/apk/res/android" | ||
| + android:id="@+id/bb_bottom_bar_outer_container" | ||
| + android:layout_width="match_parent" | ||
| + android:layout_height="wrap_content" | ||
| + android:layout_gravity="bottom"> | ||
| + | ||
| + <FrameLayout | ||
| + android:id="@+id/bb_bottom_bar_background_view" | ||
| + android:layout_width="match_parent" | ||
| + android:layout_height="match_parent" | ||
| + android:background="#FFFFFF" /> | ||
| + | ||
| + <FrameLayout | ||
| + android:id="@+id/bb_bottom_bar_background_overlay" | ||
| + android:layout_width="match_parent" | ||
| + android:layout_height="match_parent" | ||
| + android:src="@drawable/bb_bottom_bar_top_shadow" | ||
| + android:visibility="invisible" /> | ||
| + | ||
| + <LinearLayout | ||
| + android:id="@+id/bb_bottom_bar_item_container" | ||
| + android:layout_width="match_parent" | ||
| + android:layout_height="wrap_content" | ||
| + android:background="#00000000" | ||
| + android:gravity="center_horizontal" | ||
| + android:orientation="horizontal" /> | ||
| + | ||
| +</FrameLayout> |
0 comments on commit
bf66264