Skip to content
Browse files

Now supports autohide.

  • Loading branch information...
1 parent f16af1b commit bf66264f1f14e59cbb9b43e573acbdbb1ac3a2a8 @roughike committed
View
2 bottom-bar/bottom-bar.iml
@@ -88,8 +88,10 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="animated-vector-drawable-23.2.0" level="project" />
<orderEntry type="library" exported="" name="support-v4-23.2.0" level="project" />
+ <orderEntry type="library" exported="" name="recyclerview-v7-23.2.0" level="project" />
<orderEntry type="library" exported="" name="support-annotations-23.2.0" level="project" />
<orderEntry type="library" exported="" name="support-vector-drawable-23.2.0" level="project" />
+ <orderEntry type="library" exported="" name="design-23.2.0" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-23.2.0" level="project" />
</component>
</module>
View
1 bottom-bar/build.gradle
@@ -41,6 +41,7 @@ dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.2.0'
+ compile 'com.android.support:design:23.2.0'
}
apply plugin: 'com.github.dcendents.android-maven'
View
81 bottom-bar/src/main/java/com/roughike/bottombar/BottomBar.java
@@ -12,7 +12,9 @@
import android.support.annotation.IdRes;
import android.support.annotation.MenuRes;
import android.support.annotation.StyleRes;
+import android.support.design.widget.CoordinatorLayout;
import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -29,6 +31,8 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.roughike.bottombar.scrollsweetness.BottomNavigationBehavior;
+
import java.util.HashMap;
/*
@@ -57,6 +61,8 @@
private Context mContext;
private boolean mIsTabletMode;
+ private boolean mIsShy;
+ private boolean mUseExtraOffset;
private ViewGroup mUserContentContainer;
private View mOuterContainer;
@@ -169,6 +175,42 @@ public static BottomBar attach(View view, Bundle savedInstanceState) {
}
/**
+ * Adds the BottomBar inside of your CoordinatorLayout and shows / hides
+ * it according to scroll state changes.
+ * <p/>
+ * Remember to also call {@link #onRestoreInstanceState(Bundle)} inside
+ * of your {@link Activity#onSaveInstanceState(Bundle)} to restore the state.
+ *
+ * @param coordinatorLayout a CoordinatorLayout for the BottomBar to add itself into
+ * @param savedInstanceState a Bundle for restoring the state on configuration change.
+ * @return a BottomBar at the bottom of the screen.
+ */
+ public static BottomBar attachShy(CoordinatorLayout coordinatorLayout, Bundle savedInstanceState) {
+ final BottomBar bottomBar = new BottomBar(coordinatorLayout.getContext());
+ bottomBar.toughChildHood(ViewCompat.getFitsSystemWindows(coordinatorLayout));
+ bottomBar.onRestoreInstanceState(savedInstanceState);
+
+ bottomBar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @SuppressWarnings("deprecation")
+ @Override
+ public void onGlobalLayout() {
+ ((CoordinatorLayout.LayoutParams) bottomBar.getLayoutParams())
+ .setBehavior(new BottomNavigationBehavior(bottomBar.getOuterContainer().getHeight(), 0));
+ ViewTreeObserver obs = bottomBar.getViewTreeObserver();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ obs.removeOnGlobalLayoutListener(this);
+ } else {
+ obs.removeGlobalOnLayoutListener(this);
+ }
+ }
+ });
+
+ coordinatorLayout.addView(bottomBar);
+ return bottomBar;
+ }
+
+ /**
* Set tabs and fragments for this BottomBar. When setting more than 3 items,
* only the icons will show by default, but the selected item
* will have the text visible.
@@ -534,23 +576,40 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
private void initializeViews() {
- View rootView = View.inflate(mContext,
- R.layout.bb_bottom_bar_item_container, null);
+ View rootView = View.inflate(mContext, mIsShy?
+ R.layout.bb_bottom_bar_item_container_shy : R.layout.bb_bottom_bar_item_container, null);
mTabletRightBorder = rootView.findViewById(R.id.bb_tablet_right_border);
mIsTabletMode = mTabletRightBorder != null;
+
mUserContentContainer = (ViewGroup) rootView.findViewById(R.id.bb_user_content_container);
+ mShadowView = rootView.findViewById(R.id.bb_bottom_bar_shadow);
+
mOuterContainer = rootView.findViewById(R.id.bb_bottom_bar_outer_container);
mItemContainer = (ViewGroup) rootView.findViewById(R.id.bb_bottom_bar_item_container);
mBackgroundView = rootView.findViewById(R.id.bb_bottom_bar_background_view);
mBackgroundOverlay = rootView.findViewById(R.id.bb_bottom_bar_background_overlay);
- mShadowView = rootView.findViewById(R.id.bb_bottom_bar_shadow);
-
addView(rootView);
}
+ /**
+ * Makes this BottomBar "shy". In other words, it hides on scroll.
+ */
+ private void toughChildHood(boolean useExtraOffset) {
+ mIsShy = true;
+ mUseExtraOffset = useExtraOffset;
+ }
+
+ protected boolean isShy() {
+ return mIsShy;
+ }
+
+ protected boolean useExtraOffset() {
+ return mUseExtraOffset;
+ }
+
protected ViewGroup getUserContainer() {
return mUserContentContainer;
}
@@ -926,7 +985,7 @@ private void clearItems() {
}
}
- private static void navBarMagic(Activity activity, BottomBar bottomBar) {
+ private static void navBarMagic(Activity activity, final BottomBar bottomBar) {
Resources res = activity.getResources();
int softMenuIdentifier = res
.getIdentifier("config_showNavigationBar", "bool", "android");
@@ -1009,8 +1068,16 @@ private static void navBarMagic(Activity activity, BottomBar bottomBar) {
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
- outerContainer.getLayoutParams().height =
- outerContainer.getHeight() + navBarHeightCopy;
+ int newHeight = outerContainer.getHeight() + navBarHeightCopy;
+ outerContainer.getLayoutParams().height = newHeight;
+
+ if (bottomBar.isShy()) {
+ int defaultOffset = bottomBar.useExtraOffset()? navBarHeightCopy : 0;
+ bottomBar.setTranslationY(defaultOffset);
+ ((CoordinatorLayout.LayoutParams) bottomBar.getLayoutParams())
+ .setBehavior(new BottomNavigationBehavior(newHeight, defaultOffset));
+ }
+
ViewTreeObserver obs = outerContainer.getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
View
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();
+ }
+ }
+}
View
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);
+ }
+
+}
View
3 bottom-bar/src/main/res/layout/bb_bottom_bar_item_container.xml
@@ -7,8 +7,7 @@
<FrameLayout
android:id="@+id/bb_user_content_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_above="@+id/bb_bottom_bar_outer_container">
+ android:layout_height="match_parent">
<ImageView
android:id="@+id/bb_bottom_bar_shadow"
View
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

Please sign in to comment.
Something went wrong with that request. Please try again.