From f8b0ae210e45ba9f0e32e6454df71781ea851205 Mon Sep 17 00:00:00 2001 From: Xana Hopper Date: Wed, 30 Aug 2017 21:12:16 +0800 Subject: [PATCH 1/3] Behavior of BottomNavigation and FAB modified to better SnackBar show. --- ahbottomnavigation/build.gradle | 18 +-- .../AHBottomNavigationBehavior.java | 114 ++++++++-------- .../AHBottomNavigationFABBehavior.java | 122 ++++++++++++------ build.gradle | 1 + demo/build.gradle | 9 +- .../ahbottomnavigation/demo/DemoActivity.java | 23 ++++ demo/src/main/res/layout/activity_home.xml | 21 ++- demo/src/main/res/values-v19/styles.xml | 2 +- demo/src/main/res/values-v21/styles.xml | 2 +- 9 files changed, 201 insertions(+), 111 deletions(-) diff --git a/ahbottomnavigation/build.gradle b/ahbottomnavigation/build.gradle index b92b37b1..ecbcfef0 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.0.6' + libraryVersion = '2.0.11' - 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' @@ -31,8 +31,8 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion 25 - versionCode 39 - versionName "2.0.6" + versionCode 43 + versionName "2.0.11" } buildTypes { release { diff --git a/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationBehavior.java b/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationBehavior.java index 579e5372..8f4ac0b2 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,7 +24,7 @@ /** * */ -public class AHBottomNavigationBehavior extends VerticalScrollingBehavior { +public class AHBottomNavigationBehavior extends CoordinatorLayout.Behavior { private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator(); private static final int ANIM_DURATION = 300; @@ -77,69 +78,45 @@ private TabLayout findTabLayout(View child) { @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 + @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; + if (dependency != null) { + return dependency instanceof Snackbar.SnackbarLayout || + dependency instanceof AppBarLayout; } 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 * @@ -304,8 +281,39 @@ public void updateSnackbar(final View child, View dependency) { } } } - - /** + + /** + * 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); + if (height != 0) { + float translationY = child.getHeight() * top / height; + child.setTranslationY(-translationY); + } + } + } + + private int getAppBarLayoutExpendHeight(AppBarLayout layout) { + int height = 0; + for (int i = 0; i < layout.getChildCount(); i++) { + View child = layout.getChildAt(i); + AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams) child.getLayoutParams(); + if ((lp.getScrollFlags() & AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL) != 0) { + height += child.getHeight(); + } + } + return height; + } + + /** * Is hidden * @return */ diff --git a/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationFABBehavior.java b/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationFABBehavior.java index 711a4d22..6b721d35 100644 --- a/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationFABBehavior.java +++ b/ahbottomnavigation/src/main/java/com/aurelhubert/ahbottomnavigation/AHBottomNavigationFABBehavior.java @@ -1,57 +1,95 @@ 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; + } + + @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; + 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) { + Log.d("Behavior", "dependency snackbar: " + dependency.getY()); + 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) { + ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) child.getLayoutParams(); + int fabDefaultBottomMargin = p.bottomMargin; + child.setY(dependency.getY() - fabDefaultBottomMargin); + } + } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 99186f46..0f26ad19 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,7 @@ buildscript { allprojects { repositories { jcenter() + maven { url "https://jitpack.io" } } } diff --git a/demo/build.gradle b/demo/build.gradle index 1f09bc76..5ae2de00 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -24,8 +24,11 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:25.0.0' - compile 'com.android.support:cardview-v7:25.0.0' - compile 'com.android.support:design:25.0.0' + compile 'com.android.support:appcompat-v7:25.2.0' + compile 'com.android.support:cardview-v7:25.2.0' + compile 'com.android.support:design:25.2.0' + + compile 'com.github.matecode:Snacky:1.0.2' +// compile 'tech.xana:ahbottomnavigation:2.0.7' compile project(':ahbottomnavigation') } 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 e94522bf..a7d1966a 100644 --- a/demo/src/main/java/com/aurelhubert/ahbottomnavigation/demo/DemoActivity.java +++ b/demo/src/main/java/com/aurelhubert/ahbottomnavigation/demo/DemoActivity.java @@ -7,9 +7,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.view.View; import android.view.animation.OvershootInterpolator; @@ -21,11 +23,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; @@ -60,6 +66,14 @@ 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); if (useMenuResource) { tabColors = getApplicationContext().getResources().getIntArray(R.array.tab_colors); @@ -100,6 +114,15 @@ public boolean onTabSelected(int position, boolean wasSelected) { viewPager.setCurrentItem(position, false); currentFragment = adapter.getCurrentFragment(); currentFragment.willBeDisplayed(); + floatingActionButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Snacky.builder() + .setView(bottomNavigation) + .success() + .setText("实验").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"/> -