diff --git a/ahbottomnavigation/build.gradle b/ahbottomnavigation/build.gradle index b5078c84..febee6a3 100644 --- a/ahbottomnavigation/build.gradle +++ b/ahbottomnavigation/build.gradle @@ -4,20 +4,20 @@ ext { bintrayRepo = 'maven' bintrayName = 'ah-bottom-navigation' - publishedGroupId = 'com.aurelhubert' + publishedGroupId = 'tech.xana.android' libraryName = 'AHBottomNavigation' artifact = 'ahbottomnavigation' libraryDescription = 'A library to reproduce the behavior of the Bottom Navigation guidelines from Material Design.' - siteUrl = 'https://github.com/aurelhubert/ahbottomnavigation' - gitUrl = 'https://github.com/aurelhubert/ahbottomnavigation.git' + siteUrl = 'https://github.com/xanahopper/ahbottomnavigation' + gitUrl = 'https://github.com/xanahopper/ahbottomnavigation.git' libraryVersion = '2.1.0' - developerId = 'aurelhubert' - developerName = 'Aurelien Hubert' - developerEmail = 'aurel.hubert@gmail.com' + developerId = 'xanahopper' + developerName = 'Xana Hopper' + developerEmail = 'xanahopper@163.com' licenseName = 'The Apache Software License, Version 2.0' licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' diff --git a/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigation.java b/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigation.java index b502ad1e..e4ffeaff 100644 --- a/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigation.java +++ b/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigation.java @@ -78,6 +78,7 @@ public enum TitleState { private ArrayList items = new ArrayList<>(); private ArrayList views = new ArrayList<>(); private AHBottomNavigationBehavior bottomNavigationBehavior; + private AHBottomNavigationFABBehavior fabBehavior; private LinearLayout linearLayoutContainer; private View backgroundColorView; private Animator circleRevealAnim; @@ -317,7 +318,12 @@ private int calculateHeight(int layoutHeight) { } typedValue.recycle(); - + if (bottomNavigationBehavior != null && translucentNavigation) { + bottomNavigationBehavior.setBehaviorTranslationEnabled(translucentNavigationEnabled, navigationBarHeight); + } + if (fabBehavior != null && translucentNavigation) { + fabBehavior.setNavigationBarHeight(navigationBarHeight); + } return layoutHeight; } @@ -1250,7 +1256,7 @@ public void setBehaviorTranslationEnabled(boolean behaviorTranslationEnabled) { */ public void manageFloatingActionButtonBehavior(FloatingActionButton fab) { if (fab.getParent() instanceof CoordinatorLayout) { - AHBottomNavigationFABBehavior fabBehavior = new AHBottomNavigationFABBehavior(navigationBarHeight); + fabBehavior = new AHBottomNavigationFABBehavior(navigationBarHeight); ((CoordinatorLayout.LayoutParams) fab.getLayoutParams()) .setBehavior(fabBehavior); } diff --git a/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationBehavior.java b/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationBehavior.java index 579e5372..89eae1e3 100644 --- a/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationBehavior.java +++ b/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationBehavior.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.os.Build; +import android.support.design.widget.AppBarLayout; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; @@ -23,293 +24,307 @@ /** * */ -public class AHBottomNavigationBehavior extends VerticalScrollingBehavior { - - private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator(); - private static final int ANIM_DURATION = 300; - - private int mTabLayoutId; - private boolean hidden = false; - private ViewPropertyAnimatorCompat translationAnimator; - private ObjectAnimator translationObjectAnimator; - private TabLayout mTabLayout; - private Snackbar.SnackbarLayout snackbarLayout; - private FloatingActionButton floatingActionButton; - private int mSnackbarHeight = -1, navigationBarHeight = 0; - private boolean fabBottomMarginInitialized = false; - private float targetOffset = 0, fabTargetOffset = 0, fabDefaultBottomMargin = 0, snackBarY = 0; - private boolean behaviorTranslationEnabled = true; - private OnNavigationPositionListener navigationPositionListener; - - /** - * Constructor - */ - public AHBottomNavigationBehavior() { - super(); - } - - public AHBottomNavigationBehavior(boolean behaviorTranslationEnabled, int navigationBarHeight) { - super(); - this.behaviorTranslationEnabled = behaviorTranslationEnabled; - this.navigationBarHeight = navigationBarHeight; - } - - public AHBottomNavigationBehavior(Context context, AttributeSet attrs) { - super(context, attrs); - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AHBottomNavigationBehavior_Params); - mTabLayoutId = a.getResourceId(R.styleable.AHBottomNavigationBehavior_Params_tabLayoutId, View.NO_ID); - a.recycle(); - } - - @Override - public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) { - boolean layoutChild = super.onLayoutChild(parent, child, layoutDirection); - if (mTabLayout == null && mTabLayoutId != View.NO_ID) { - mTabLayout = findTabLayout(child); - } - return layoutChild; - } - - private TabLayout findTabLayout(View child) { - if (mTabLayoutId == 0) return null; - return (TabLayout) child.findViewById(mTabLayoutId); - } - - @Override - public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) { - return super.onDependentViewChanged(parent, child, dependency); - } - - @Override - public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) { - super.onDependentViewRemoved(parent, child, dependency); - } - - @Override - public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) { - if (dependency != null && dependency instanceof Snackbar.SnackbarLayout) { - updateSnackbar(child, dependency); - return true; - } - return super.layoutDependsOn(parent, child, dependency); - } - - @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) { - } - - @Override - protected boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, @ScrollDirection int scrollDirection) { - return false; - } - - @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 (dyConsumed < 0) { - handleDirection(child, ScrollDirection.SCROLL_DIRECTION_DOWN); - } else if (dyConsumed > 0) { - handleDirection(child, ScrollDirection.SCROLL_DIRECTION_UP); - } - } - - @Override - public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { - return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); - } - - /** - * Handle scroll direction - * @param child - * @param scrollDirection - */ - private void handleDirection(V child, int scrollDirection) { - if (!behaviorTranslationEnabled) { - return; - } - if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_DOWN && hidden) { - hidden = false; - animateOffset(child, 0, false, true); - } else if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_UP && !hidden) { - hidden = true; - animateOffset(child, child.getHeight(), false, true); - } - } - - /** - * Animate offset - * - * @param child - * @param offset - */ - private void animateOffset(final V child, final int offset, boolean forceAnimation, boolean withAnimation) { - if (!behaviorTranslationEnabled && !forceAnimation) { - return; - } - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - ensureOrCancelObjectAnimation(child, offset, withAnimation); - translationObjectAnimator.start(); - } else { - ensureOrCancelAnimator(child, withAnimation); - translationAnimator.translationY(offset).start(); - } - } - - /** - * Manage animation for Android >= KITKAT - * - * @param child - */ - private void ensureOrCancelAnimator(V child, boolean withAnimation) { - if (translationAnimator == null) { - translationAnimator = ViewCompat.animate(child); - translationAnimator.setDuration(withAnimation ? ANIM_DURATION : 0); - translationAnimator.setUpdateListener(new ViewPropertyAnimatorUpdateListener() { - @Override - public void onAnimationUpdate(View view) { - if (navigationPositionListener != null) { - navigationPositionListener.onPositionChange((int) (view.getMeasuredHeight() - view.getTranslationY() + snackBarY)); - } - } - }); - translationAnimator.setInterpolator(INTERPOLATOR); - } else { - translationAnimator.setDuration(withAnimation ? ANIM_DURATION : 0); - translationAnimator.cancel(); - } - } - - /** - * Manage animation for Android < KITKAT - * - * @param child - */ - private void ensureOrCancelObjectAnimation(final V child, final int offset, boolean withAnimation) { - - if (translationObjectAnimator != null) { - translationObjectAnimator.cancel(); - } - - translationObjectAnimator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, offset); - translationObjectAnimator.setDuration(withAnimation ? ANIM_DURATION : 0); - translationObjectAnimator.setInterpolator(INTERPOLATOR); - translationObjectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - if (snackbarLayout != null && snackbarLayout.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - targetOffset = child.getMeasuredHeight() - child.getTranslationY(); - ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) snackbarLayout.getLayoutParams(); - p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) targetOffset); - snackbarLayout.requestLayout(); - } - // Pass navigation height to listener - if (navigationPositionListener != null) { - navigationPositionListener.onPositionChange((int) (child.getMeasuredHeight() - child.getTranslationY() + snackBarY)); - } - } - }); - } - - - public static AHBottomNavigationBehavior from(V view) { - ViewGroup.LayoutParams params = view.getLayoutParams(); - if (!(params instanceof CoordinatorLayout.LayoutParams)) { - throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); - } - CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params) - .getBehavior(); - if (!(behavior instanceof AHBottomNavigationBehavior)) { - throw new IllegalArgumentException( - "The view is not associated with AHBottomNavigationBehavior"); - } - return (AHBottomNavigationBehavior) behavior; - } - - public void setTabLayoutId(int tabId) { - this.mTabLayoutId = tabId; - } - - /** - * Enable or not the behavior translation - * @param behaviorTranslationEnabled - */ - public void setBehaviorTranslationEnabled(boolean behaviorTranslationEnabled, int navigationBarHeight) { - this.behaviorTranslationEnabled = behaviorTranslationEnabled; - this.navigationBarHeight = navigationBarHeight; - } - - /** - * Set OnNavigationPositionListener - */ - public void setOnNavigationPositionListener(OnNavigationPositionListener navigationHeightListener) { - this.navigationPositionListener = navigationHeightListener; - } - - /** - * Remove OnNavigationPositionListener() - */ - public void removeOnNavigationPositionListener() { - this.navigationPositionListener = null; - } - - /** - * Hide AHBottomNavigation with animation - * @param view - * @param offset - */ - public void hideView(V view, int offset, boolean withAnimation) { - if (!hidden) { - hidden = true; - animateOffset(view, offset, true, withAnimation); - } - } - - /** - * Reset AHBottomNavigation position with animation - * @param view - */ - public void resetOffset(V view, boolean withAnimation) { - if (hidden) { - hidden = false; - animateOffset(view, 0, true, withAnimation); - } - } - - /** - * Update Snackbar bottom margin - */ - public void updateSnackbar(final View child, View dependency) { - - if (dependency != null && dependency instanceof Snackbar.SnackbarLayout) { - - snackbarLayout = (Snackbar.SnackbarLayout) dependency; - - if (mSnackbarHeight == -1) { - mSnackbarHeight = dependency.getHeight(); - } - - int targetMargin = (int) (child.getMeasuredHeight() - child.getTranslationY()); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - child.bringToFront(); - } - - if (dependency.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) dependency.getLayoutParams(); - p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, targetMargin); - dependency.requestLayout(); - } - } - } - - /** - * Is hidden - * @return - */ - public boolean isHidden() { - return hidden; - } +public class AHBottomNavigationBehavior extends CoordinatorLayout.Behavior { + + private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator(); + private static final int ANIM_DURATION = 300; + + private int mTabLayoutId; + private boolean hidden = false; + private ViewPropertyAnimatorCompat translationAnimator; + private ObjectAnimator translationObjectAnimator; + private TabLayout mTabLayout; + private Snackbar.SnackbarLayout snackbarLayout; + private FloatingActionButton floatingActionButton; + private int mSnackbarHeight = -1, navigationBarHeight = 0; + private boolean fabBottomMarginInitialized = false; + private float targetOffset = 0, fabTargetOffset = 0, fabDefaultBottomMargin = 0, snackBarY = 0; + private boolean behaviorTranslationEnabled = true; + private OnNavigationPositionListener navigationPositionListener; + + /** + * Constructor + */ + public AHBottomNavigationBehavior() { + super(); + } + + public AHBottomNavigationBehavior(boolean behaviorTranslationEnabled, int navigationBarHeight) { + super(); + this.behaviorTranslationEnabled = behaviorTranslationEnabled; + this.navigationBarHeight = navigationBarHeight; + } + + public AHBottomNavigationBehavior(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AHBottomNavigationBehavior_Params); + mTabLayoutId = a.getResourceId(R.styleable.AHBottomNavigationBehavior_Params_tabLayoutId, View.NO_ID); + a.recycle(); + } + + public static AHBottomNavigationBehavior from(V view) { + ViewGroup.LayoutParams params = view.getLayoutParams(); + if (!(params instanceof CoordinatorLayout.LayoutParams)) { + throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); + } + CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params) + .getBehavior(); + if (!(behavior instanceof AHBottomNavigationBehavior)) { + throw new IllegalArgumentException( + "The view is not associated with AHBottomNavigationBehavior"); + } + return (AHBottomNavigationBehavior) behavior; + } + + @Override + public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) { + boolean layoutChild = super.onLayoutChild(parent, child, layoutDirection); + if (mTabLayout == null && mTabLayoutId != View.NO_ID) { + mTabLayout = findTabLayout(child); + } + return layoutChild; + } + + private TabLayout findTabLayout(View child) { + if (mTabLayoutId == 0) return null; + return (TabLayout) child.findViewById(mTabLayoutId); + } + + @Override + public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) { + if (dependency != null) { + if (dependency instanceof Snackbar.SnackbarLayout) { + updateSnackbar(child, dependency); + return false; + } else if (dependency instanceof AppBarLayout) { + updateNavigationPosition(child, dependency); + + if (child instanceof AHBottomNavigation) { + Snackbar.SnackbarLayout snackbarLayout = null; + for (View view : parent.getDependencies(child)) { + if (view instanceof Snackbar.SnackbarLayout) { + snackbarLayout = (Snackbar.SnackbarLayout) view; + break; + } + } + if (snackbarLayout != null) { + updateSnackbar(child, snackbarLayout); + } + } + return false; + } + } + return super.onDependentViewChanged(parent, child, dependency); + } + + @Override + public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) { + super.onDependentViewRemoved(parent, child, dependency); + } + + @Override + public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) { + if (dependency != null) { + return dependency instanceof Snackbar.SnackbarLayout || + dependency instanceof AppBarLayout; + } + return super.layoutDependsOn(parent, child, dependency); + } + + /** + * Animate offset + * + * @param child + * @param offset + */ + private void animateOffset(final V child, final int offset, boolean forceAnimation, boolean withAnimation) { + if (!behaviorTranslationEnabled && !forceAnimation) { + return; + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + ensureOrCancelObjectAnimation(child, offset, withAnimation); + translationObjectAnimator.start(); + } else { + ensureOrCancelAnimator(child, withAnimation); + translationAnimator.translationY(offset).start(); + } + } + + /** + * Manage animation for Android >= KITKAT + * + * @param child + */ + private void ensureOrCancelAnimator(V child, boolean withAnimation) { + if (translationAnimator == null) { + translationAnimator = ViewCompat.animate(child); + translationAnimator.setDuration(withAnimation ? ANIM_DURATION : 0); + translationAnimator.setUpdateListener(new ViewPropertyAnimatorUpdateListener() { + @Override + public void onAnimationUpdate(View view) { + if (navigationPositionListener != null) { + navigationPositionListener.onPositionChange((int) (view.getMeasuredHeight() - view + .getTranslationY() + snackBarY)); + } + } + }); + translationAnimator.setInterpolator(INTERPOLATOR); + } else { + translationAnimator.setDuration(withAnimation ? ANIM_DURATION : 0); + translationAnimator.cancel(); + } + } + + /** + * Manage animation for Android < KITKAT + * + * @param child + */ + private void ensureOrCancelObjectAnimation(final V child, final int offset, boolean withAnimation) { + + if (translationObjectAnimator != null) { + translationObjectAnimator.cancel(); + } + + translationObjectAnimator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, offset); + translationObjectAnimator.setDuration(withAnimation ? ANIM_DURATION : 0); + translationObjectAnimator.setInterpolator(INTERPOLATOR); + translationObjectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (snackbarLayout != null && snackbarLayout.getLayoutParams() instanceof ViewGroup + .MarginLayoutParams) { + targetOffset = child.getMeasuredHeight() - child.getTranslationY(); + ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) snackbarLayout.getLayoutParams(); + p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) targetOffset); + snackbarLayout.requestLayout(); + } + // Pass navigation height to listener + if (navigationPositionListener != null) { + navigationPositionListener.onPositionChange((int) (child.getMeasuredHeight() - child + .getTranslationY() + snackBarY)); + } + } + }); + } + + public void setTabLayoutId(int tabId) { + this.mTabLayoutId = tabId; + } + + /** + * Enable or not the behavior translation + * + * @param behaviorTranslationEnabled + */ + public void setBehaviorTranslationEnabled(boolean behaviorTranslationEnabled, int navigationBarHeight) { + this.behaviorTranslationEnabled = behaviorTranslationEnabled; + this.navigationBarHeight = navigationBarHeight; + } + + /** + * Set OnNavigationPositionListener + */ + public void setOnNavigationPositionListener(OnNavigationPositionListener navigationHeightListener) { + this.navigationPositionListener = navigationHeightListener; + } + + /** + * Remove OnNavigationPositionListener() + */ + public void removeOnNavigationPositionListener() { + this.navigationPositionListener = null; + } + + /** + * Hide AHBottomNavigation with animation + * + * @param view + * @param offset + */ + public void hideView(V view, int offset, boolean withAnimation) { + if (!hidden) { + hidden = true; + animateOffset(view, offset, true, withAnimation); + } + } + + /** + * Reset AHBottomNavigation position with animation + * + * @param view + */ + public void resetOffset(V view, boolean withAnimation) { + if (hidden) { + hidden = false; + animateOffset(view, 0, true, withAnimation); + } + } + + /** + * Update Snackbar bottom margin + * + * @param child BottomNavigation + * @param dependency SnackbarLayout + */ + public void updateSnackbar(final View child, View dependency) { + + if (dependency != null && dependency instanceof Snackbar.SnackbarLayout) { + + snackbarLayout = (Snackbar.SnackbarLayout) dependency; + + if (mSnackbarHeight == -1) { + mSnackbarHeight = dependency.getHeight(); + } + + int targetMargin = (int) (child.getMeasuredHeight() - child.getTranslationY()); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + child.bringToFront(); + } + + if (dependency.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) dependency.getLayoutParams(); + p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, targetMargin); + dependency.requestLayout(); + } + + if (behaviorTranslationEnabled && targetMargin < navigationBarHeight) { + snackbarLayout.setPadding(snackbarLayout.getPaddingLeft(), snackbarLayout.getPaddingTop(), + snackbarLayout.getPaddingRight(), navigationBarHeight - targetMargin); + } else { + snackbarLayout.setPadding(snackbarLayout.getPaddingLeft(), snackbarLayout.getPaddingTop(), + snackbarLayout.getPaddingRight(), 0); + } + } + } + + /** + * Update BottomNavigation when AppBarLayout scrolls + * + * @param child BottomNavigation + * @param dependency AppBarLayout + */ + private void updateNavigationPosition(V child, View dependency) { + if (dependency != null && dependency instanceof AppBarLayout) { + AppBarLayout layout = (AppBarLayout) dependency; + int top = ((AppBarLayout.Behavior) ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()) + .getBehavior()).getTopAndBottomOffset(); +// int height = dependency.getHeight(); +// int height = getAppBarLayoutExpendHeight(layout); + int height = layout.getTotalScrollRange(); + if (height > 0) { + float translationY = child.getHeight() * top / height; + child.setTranslationY(-translationY >= 0 ? -translationY : 0); + } + } + } + + /** + * Is hidden + * + * @return + */ + public boolean isHidden() { + return hidden; + } } \ No newline at end of file diff --git a/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationFABBehavior.java b/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationFABBehavior.java index 711a4d22..b6c327d4 100644 --- a/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationFABBehavior.java +++ b/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationFABBehavior.java @@ -1,57 +1,118 @@ package com.aurelhubert.ahbottomnavigation; +import android.animation.Animator; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.support.v4.view.animation.FastOutSlowInInterpolator; +import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.animation.Interpolator; /** * */ public class AHBottomNavigationFABBehavior extends CoordinatorLayout.Behavior { - private int navigationBarHeight = 0; - private long lastSnackbarUpdate = 0; - - public AHBottomNavigationFABBehavior(int navigationBarHeight) { - this.navigationBarHeight = navigationBarHeight; - } - - @Override - public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) { - if (dependency != null && dependency instanceof Snackbar.SnackbarLayout) { - return true; - } else if (dependency != null && dependency instanceof AHBottomNavigation) { - return true; - } - return super.layoutDependsOn(parent, child, dependency); - } - - @Override - public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) { - updateFloatingActionButton(child, dependency); - return super.onDependentViewChanged(parent, child, dependency); - } - - /** - * Update floating action button bottom margin - */ - private void updateFloatingActionButton(FloatingActionButton child, View dependency) { - if (child != null && dependency != null && dependency instanceof Snackbar.SnackbarLayout) { - lastSnackbarUpdate = System.currentTimeMillis(); - ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) child.getLayoutParams(); - int fabDefaultBottomMargin = p.bottomMargin; - child.setY(dependency.getY() - fabDefaultBottomMargin); - } else if (child != null && dependency != null && dependency instanceof AHBottomNavigation) { - // Hack to avoid moving the FAB when the AHBottomNavigation is moving (showing or hiding animation) - if (System.currentTimeMillis() - lastSnackbarUpdate < 30) { - return; - } - ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) child.getLayoutParams(); - int fabDefaultBottomMargin = p.bottomMargin; - child.setY(dependency.getY() - fabDefaultBottomMargin); - } - } + private int navigationBarHeight = 0; + private long lastSnackbarUpdate = 0; + private Interpolator mInterpolator = new FastOutSlowInInterpolator(); + private ViewPropertyAnimatorCompat mAnimator = null; + + public AHBottomNavigationFABBehavior(int navigationBarHeight) { + this.navigationBarHeight = navigationBarHeight; + Log.d("Behavior", "navigationHeight: " + String.valueOf(navigationBarHeight)); + } + + public void setNavigationBarHeight(int navigationBarHeight) { + this.navigationBarHeight = navigationBarHeight; + } + + @Override + public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) { + if (dependency != null && dependency instanceof Snackbar.SnackbarLayout) { + return true; + } else if (dependency != null && dependency instanceof AHBottomNavigation) { + return true; + } + return super.layoutDependsOn(parent, child, dependency); + } + + @Override + public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) { + View depView = dependency; + for (View view : parent.getDependencies(child)) { + if (view instanceof Snackbar.SnackbarLayout) { + depView = view; + break; + } + } + ensureOrCancelAnimator(child); + updateFloatingActionButton(child, depView); + return super.onDependentViewChanged(parent, child, dependency); + } + + @Override + public void onDependentViewRemoved(CoordinatorLayout parent, FloatingActionButton child, View dependency) { + lastSnackbarUpdate = System.currentTimeMillis(); + ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) child.getLayoutParams(); + int fabDefaultBottomMargin = p.bottomMargin; + float targetY = dependency.getY() - fabDefaultBottomMargin; + int targetMargin = getTargetMargin(parent, child); + if (targetMargin < navigationBarHeight) { + targetY -= (navigationBarHeight - targetMargin); + } + ensureOrCancelAnimator(child); + mAnimator.y(targetY) + .setDuration(255) + .setInterpolator(mInterpolator); + + + super.onDependentViewRemoved(parent, child, dependency); + } + + private void ensureOrCancelAnimator(FloatingActionButton child) { + if (mAnimator == null) { + mAnimator = ViewCompat.animate(child); + mAnimator.setDuration(255) + .setInterpolator(mInterpolator); + } else { + mAnimator.cancel(); + } + } + + /** + * Update floating action button bottom margin + */ + private void updateFloatingActionButton(FloatingActionButton child, View dependency) { + if (child != null && dependency != null && dependency instanceof Snackbar.SnackbarLayout) { + lastSnackbarUpdate = System.currentTimeMillis(); + ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) child.getLayoutParams(); + int fabDefaultBottomMargin = p.bottomMargin; + int targetY = (int) (dependency.getY() - fabDefaultBottomMargin); + child.setY(targetY); + } else if (child != null && dependency != null && dependency instanceof AHBottomNavigation) { + ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) child.getLayoutParams(); + int fabDefaultBottomMargin = p.bottomMargin; + int targetMargin = (int)(dependency.getMeasuredHeight() - dependency.getTranslationY()); + int targetY = (int) (dependency.getY() - fabDefaultBottomMargin); + if (targetMargin < navigationBarHeight) { + targetY -= (navigationBarHeight - targetMargin); + } + child.setY(targetY); + } + } + + private int getTargetMargin(CoordinatorLayout parent, FloatingActionButton child) { + for (View view: parent.getDependencies(child)) { + if (view instanceof AHBottomNavigation) { + return (int) (view.getMeasuredHeight() - view.getTranslationY()); + } + } + return 0; + } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index e706f6cd..08ccd0b2 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,7 @@ buildscript { allprojects { repositories { jcenter() + maven { url "https://jitpack.io" } } } diff --git a/demo/src/main/java/com/aurelhubert/ahbottomnavigation/demo/DemoActivity.java b/demo/src/main/java/com/aurelhubert/ahbottomnavigation/demo/DemoActivity.java index a7de0aad..5a4b0539 100644 --- a/demo/src/main/java/com/aurelhubert/ahbottomnavigation/demo/DemoActivity.java +++ b/demo/src/main/java/com/aurelhubert/ahbottomnavigation/demo/DemoActivity.java @@ -8,9 +8,11 @@ import android.os.Handler; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; +import android.support.design.widget.TabLayout; import android.support.v4.content.ContextCompat; import android.support.v4.view.animation.LinearOutSlowInInterpolator; import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; import android.support.v7.app.AppCompatDelegate; import android.view.View; import android.view.animation.OvershootInterpolator; @@ -23,11 +25,15 @@ import java.util.ArrayList; +import de.mateware.snacky.Snacky; + public class DemoActivity extends AppCompatActivity { private DemoFragment currentFragment; private DemoViewPagerAdapter adapter; private AHBottomNavigationAdapter navigationAdapter; + private Toolbar mToolbar; + private TabLayout mTabLayout; private ArrayList bottomNavigationItems = new ArrayList<>(); private boolean useMenuResource = true; private int[] tabColors; @@ -66,6 +72,15 @@ private void initUI() { bottomNavigation = (AHBottomNavigation) findViewById(R.id.bottom_navigation); viewPager = (AHBottomNavigationViewPager) findViewById(R.id.view_pager); floatingActionButton = (FloatingActionButton) findViewById(R.id.floating_action_button); + mToolbar = (Toolbar) findViewById(R.id.toolbar); + mTabLayout = (TabLayout) findViewById(R.id.tablayout); + + mTabLayout.addTab(mTabLayout.newTab().setText("Tab 1")); + mTabLayout.addTab(mTabLayout.newTab().setText("Tab 2")); + mTabLayout.addTab(mTabLayout.newTab().setText("Tab 3")); + + setSupportActionBar(mToolbar); + getSupportActionBar().setDisplayShowTitleEnabled(true); if (useMenuResource) { tabColors = getApplicationContext().getResources().getIntArray(R.array.tab_colors); @@ -111,6 +126,23 @@ public boolean onTabSelected(int position, boolean wasSelected) { currentFragment = adapter.getCurrentFragment(); currentFragment.willBeDisplayed(); + floatingActionButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final Snackbar snackbar = Snacky.builder() + .setView(bottomNavigation) + .setDuration(Snacky.LENGTH_INDEFINITE) + .success() + .setText("实验"); + snackbar.setAction("关闭", new View.OnClickListener() { + @Override + public void onClick(View v) { + snackbar.dismiss(); + } + }); + snackbar.show(); + } + }); if (position == 1) { bottomNavigation.setNotification("", 1); diff --git a/demo/src/main/res/layout/activity_home.xml b/demo/src/main/res/layout/activity_home.xml index 9129c696..824d4797 100644 --- a/demo/src/main/res/layout/activity_home.xml +++ b/demo/src/main/res/layout/activity_home.xml @@ -5,16 +5,33 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"/> -