From e6bbbea8106eb8423532eb27e04e210d39e35bef Mon Sep 17 00:00:00 2001 From: GIGAMOLE Date: Sun, 5 Jun 2016 02:46:20 +0300 Subject: [PATCH] BottomBehavior support Fixes Optimizations Code refactor --- README.md | 44 +- .../HorizontalCoordinatorNtbActivity.java | 42 +- .../HorizontalNtbActivity.java | 19 +- .../navigationtabbar/SamplesNtbActivity.java | 2 +- .../TopHorizontalNtbActivity.java | 6 +- .../navigationtabbar/VerticalNtbActivity.java | 2 +- .../activity_horizontal_coordinator_ntb.xml | 76 ++- .../res/layout/activity_horizontal_ntb.xml | 44 +- .../layout/activity_horizontal_top_ntb.xml | 10 +- .../main/res/layout/activity_samples_ntb.xml | 12 +- .../main/res/layout/activity_vertical_ntb.xml | 2 +- app/src/main/res/values/dimens.xml | 4 - library/build.gradle | 5 +- .../BottomNavigationTabBarBehavior.java | 406 ---------------- .../library/VerticalScrollingBehavior.java | 146 ------ .../behavior/NavigationTabBarBehavior.java | 293 ++++++++++++ .../behavior/VerticalScrollingBehavior.java | 128 +++++ .../library/{ => ntb}/NavigationTabBar.java | 445 +++++++++--------- 18 files changed, 758 insertions(+), 928 deletions(-) delete mode 100644 app/src/main/res/values/dimens.xml delete mode 100644 library/src/main/java/com/gigamole/library/BottomNavigationTabBarBehavior.java delete mode 100644 library/src/main/java/com/gigamole/library/VerticalScrollingBehavior.java create mode 100644 library/src/main/java/com/gigamole/library/behavior/NavigationTabBarBehavior.java create mode 100644 library/src/main/java/com/gigamole/library/behavior/VerticalScrollingBehavior.java rename library/src/main/java/com/gigamole/library/{ => ntb}/NavigationTabBar.java (87%) diff --git a/README.md b/README.md index cf7972d..1d78ee4 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,9 @@ NavigationTabBar Navigation tab bar with colorful interactions. -Horizontal NTB|NTB with selected icons |NTB with CoordinatorLayout|Vertical NTB|NTB Samples| -:-------------------------:|:-------------------------:|:-------------------------:|:-------------------------:|:-------------------------: -![](https://lh6.googleusercontent.com/-Bf7uxUiRvfk/VvpVlkZzsVI/AAAAAAAACPA/Ysg9uuBpaL8UhsXpYPlyNJK6IJssdkMvg/w325-h552-no/hntb.gif)|![](https://lh5.googleusercontent.com/-LcHHajuKNzw/Vz77El2lHsI/AAAAAAAACiQ/I0CjrMUP6R4ioH9h8nEe37LCqXmb3GJKACL0B/w317-h552-no/ntbsi.gif)|![](https://lh6.googleusercontent.com/-hMvLn-jzY3k/VzcPrGAmr4I/AAAAAAAACc0/US0yokfG23kQJEAPxFoPp-8lOUNRSPV9QCL0B/w321-h552-no/cltntb.gif)|![](https://lh4.googleusercontent.com/-k4Ac7-c2m8E/VvpVlk3ZmLI/AAAAAAAACPA/21ISoAYGZzUlvGPmIauXwfYZOKdCYIRGg/w323-h552-no/vntb.gif)|![](https://lh5.googleusercontent.com/-hmELfZQvexU/VvpVlooaPvI/AAAAAAAACPA/5HA5ic7dASwBUYqpqcfxAmfLzPPDXejqQ/w322-h552-no/ntbs.gif) - +Horizontal NTB | NTB with bottom behavior | NTB with selected icons | NTB with CoordinatorLayout | Vertical NTB | NTB Samples | +:-------------------------:|:-------------------------:|:-------------------------:|:-------------------------:|:-------------------------:|:-------------------------: +![](https://lh6.googleusercontent.com/-Bf7uxUiRvfk/VvpVlkZzsVI/AAAAAAAACPA/Ysg9uuBpaL8UhsXpYPlyNJK6IJssdkMvg/w325-h552-no/hntb.gif)|![](https://lh4.googleusercontent.com/-mF70XCnMpgk/V1NnK34tnhI/AAAAAAAACkY/z0Z15Q_7gg4fMovWiEvo9agJgz7m933cQCL0B/w323-h552-no/btbntb.gif)|![](https://lh5.googleusercontent.com/-LcHHajuKNzw/Vz77El2lHsI/AAAAAAAACiQ/I0CjrMUP6R4ioH9h8nEe37LCqXmb3GJKACL0B/w317-h552-no/ntbsi.gif)|![](https://lh6.googleusercontent.com/-hMvLn-jzY3k/VzcPrGAmr4I/AAAAAAAACc0/US0yokfG23kQJEAPxFoPp-8lOUNRSPV9QCL0B/w321-h552-no/cltntb.gif)|![](https://lh4.googleusercontent.com/-k4Ac7-c2m8E/VvpVlk3ZmLI/AAAAAAAACPA/21ISoAYGZzUlvGPmIauXwfYZOKdCYIRGg/w323-h552-no/vntb.gif)|![](https://lh5.googleusercontent.com/-hmELfZQvexU/VvpVlooaPvI/AAAAAAAACPA/5HA5ic7dASwBUYqpqcfxAmfLzPPDXejqQ/w322-h552-no/ntbs.gif) U can check the sample app [here](https://github.com/DevLight-Mobile-Agency/NavigationTabBar/tree/master/app). Download @@ -33,7 +32,7 @@ dependencies { Or Gradle Maven Central: ```groovy -compile 'com.github.devlight.navigationtabbar:library:1.1.8' +compile 'com.github.devlight.navigationtabbar:library:1.2.0' ``` Or Maven: @@ -42,7 +41,7 @@ Or Maven: com.github.devlight.navigationtabbar library - 1.1.8 + 1.2.0 aar ``` @@ -63,9 +62,17 @@ For NTB you can set such parameters as: allows you to set NTB models, where you set icon and color. Can be set up only via code. + - behavior: + + allows you to set bottom translation behavior. + - view pager: - allows you to connect NTB with ViewPager. If you want your can also set OnPageChangeListener. + allows you to connect NTB with ViewPager. If you want your can also set OnPageChangeListener. + + - background: + + allows you to set background to NTB which automatically set with offset relative to badge gravity. - model selected icon: @@ -149,6 +156,9 @@ By default badge bg color is the active model color and badge title color is the If your set ViewPager you can action down on active pointer and do like drag. +If you want to set the background to NTB, just set background via XML or code and its automatically set relative to badge gravity. + + Init Check out in code init: @@ -211,21 +221,7 @@ navigationTabBar.setBadgeBgColor(Color.RED); navigationTabBar.setBadgeTitleColor(Color.WHITE); ``` -If you want to set the background to NTB, you need to create some view at he bottom of NTB in layout and then set height of your background view like this: - -```java -navigationTabBar.post(new Runnable() { - @Override - public void run() { - final View background = findViewById(R.id.background); - background.getLayoutParams().height = (int) navigationTabBar.getBarHeight(); - background.requestLayout(); - } -}); -``` - If your models is in badge mode you can set title, hide, show, toggle and update badge title like this: - ```java model.setTitle("Here some title to model"); model.hideBadge(); @@ -234,9 +230,9 @@ model.toggleBadge(); model.updateBadgeTitle("Here some title like NEW or some integer value"); ``` -To enable translation inside CoordinatorLayout when at bottom of screen: +To enable behavior translation inside CoordinatorLayout when at bottom of screen: ```java -bottomNavigation.setBehaviorTranslationEnabled(true); +bottomNavigation.setBehaviorEnabled(true); ``` Other methods check out in sample. @@ -244,7 +240,7 @@ Other methods check out in sample. And XML init: ```xml - { diff --git a/app/src/main/java/com/gigamole/navigationtabbar/HorizontalNtbActivity.java b/app/src/main/java/com/gigamole/navigationtabbar/HorizontalNtbActivity.java index bd91aa0..11eb430 100644 --- a/app/src/main/java/com/gigamole/navigationtabbar/HorizontalNtbActivity.java +++ b/app/src/main/java/com/gigamole/navigationtabbar/HorizontalNtbActivity.java @@ -10,7 +10,7 @@ import android.view.ViewGroup; import android.widget.TextView; -import com.gigamole.library.NavigationTabBar; +import com.gigamole.library.ntb.NavigationTabBar; import java.util.ArrayList; @@ -26,14 +26,6 @@ protected void onCreate(final Bundle savedInstanceState) { initUI(); } - @Override - protected void onDestroy() { - super.onDestroy(); - System.runFinalization(); - Runtime.getRuntime().gc(); - System.gc(); - } - private void initUI() { final ViewPager viewPager = (ViewPager) findViewById(R.id.vp_horizontal_ntb); viewPager.setAdapter(new PagerAdapter() { @@ -134,15 +126,6 @@ public void onPageScrollStateChanged(final int state) { } }); - navigationTabBar.post(new Runnable() { - @Override - public void run() { - final View bgNavigationTabBar = findViewById(R.id.bg_ntb_horizontal); - bgNavigationTabBar.getLayoutParams().height = (int) navigationTabBar.getBarHeight(); - bgNavigationTabBar.requestLayout(); - } - }); - navigationTabBar.postDelayed(new Runnable() { @Override public void run() { diff --git a/app/src/main/java/com/gigamole/navigationtabbar/SamplesNtbActivity.java b/app/src/main/java/com/gigamole/navigationtabbar/SamplesNtbActivity.java index 097263f..74e2f16 100644 --- a/app/src/main/java/com/gigamole/navigationtabbar/SamplesNtbActivity.java +++ b/app/src/main/java/com/gigamole/navigationtabbar/SamplesNtbActivity.java @@ -6,7 +6,7 @@ import android.support.v4.graphics.ColorUtils; import android.widget.Toast; -import com.gigamole.library.NavigationTabBar; +import com.gigamole.library.ntb.NavigationTabBar; import java.util.ArrayList; diff --git a/app/src/main/java/com/gigamole/navigationtabbar/TopHorizontalNtbActivity.java b/app/src/main/java/com/gigamole/navigationtabbar/TopHorizontalNtbActivity.java index 6f32fc8..2c8ccaf 100644 --- a/app/src/main/java/com/gigamole/navigationtabbar/TopHorizontalNtbActivity.java +++ b/app/src/main/java/com/gigamole/navigationtabbar/TopHorizontalNtbActivity.java @@ -12,7 +12,7 @@ import android.view.ViewGroup; import android.widget.TextView; -import com.gigamole.library.NavigationTabBar; +import com.gigamole.library.ntb.NavigationTabBar; import java.util.ArrayList; import java.util.Random; @@ -110,10 +110,6 @@ public Object instantiateItem(final ViewGroup container, final int position) { navigationTabBar.post(new Runnable() { @Override public void run() { - final View bgNavigationTabBar = findViewById(R.id.bg_ntb_horizontal); - bgNavigationTabBar.getLayoutParams().height = (int) navigationTabBar.getBarHeight(); - bgNavigationTabBar.requestLayout(); - final View viewPager = findViewById(R.id.vp_horizontal_ntb); ((ViewGroup.MarginLayoutParams) viewPager.getLayoutParams()).topMargin = (int) -navigationTabBar.getBadgeMargin(); diff --git a/app/src/main/java/com/gigamole/navigationtabbar/VerticalNtbActivity.java b/app/src/main/java/com/gigamole/navigationtabbar/VerticalNtbActivity.java index 53275a7..2fbb075 100644 --- a/app/src/main/java/com/gigamole/navigationtabbar/VerticalNtbActivity.java +++ b/app/src/main/java/com/gigamole/navigationtabbar/VerticalNtbActivity.java @@ -10,7 +10,7 @@ import android.view.ViewGroup; import android.widget.TextView; -import com.gigamole.library.NavigationTabBar; +import com.gigamole.library.ntb.NavigationTabBar; import java.util.ArrayList; diff --git a/app/src/main/res/layout/activity_horizontal_coordinator_ntb.xml b/app/src/main/res/layout/activity_horizontal_coordinator_ntb.xml index 4894b16..3bed374 100644 --- a/app/src/main/res/layout/activity_horizontal_coordinator_ntb.xml +++ b/app/src/main/res/layout/activity_horizontal_coordinator_ntb.xml @@ -2,6 +2,7 @@ + + android:tint="#9f90af" + android:padding="32dp" + app:layout_collapseMode="pin"/> + app:title="@string/app_name" + app:popupTheme="@style/AppTheme.AppBarOverlay"/> @@ -46,45 +53,30 @@ android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> - - - + android:layout_marginEnd="16dp" + android:tint="#9f90af" + android:src="@drawable/ic_first" + app:backgroundTint="#605271"/> - - - - + diff --git a/app/src/main/res/layout/activity_horizontal_ntb.xml b/app/src/main/res/layout/activity_horizontal_ntb.xml index 478b2eb..5dd7f64 100644 --- a/app/src/main/res/layout/activity_horizontal_ntb.xml +++ b/app/src/main/res/layout/activity_horizontal_ntb.xml @@ -1,5 +1,5 @@ - + android:layout_height="0dp" + android:layout_weight="1"/> - + android:layout_height="60dp" + android:background="#605271" + app:ntb_badge_gravity="top" + app:ntb_badge_position="right" + app:ntb_badged="true" + app:ntb_scaled="true" + app:ntb_tinted="true" + app:ntb_title_mode="all" + app:ntb_titled="true"/> - - - - - - - + diff --git a/app/src/main/res/layout/activity_horizontal_top_ntb.xml b/app/src/main/res/layout/activity_horizontal_top_ntb.xml index ef221b7..961ee00 100644 --- a/app/src/main/res/layout/activity_horizontal_top_ntb.xml +++ b/app/src/main/res/layout/activity_horizontal_top_ntb.xml @@ -24,17 +24,11 @@ android:layout_height="wrap_content" app:layout_scrollFlags="scroll|enterAlways"> - - - - - - - - - - - - 180dp - \ No newline at end of file diff --git a/library/build.gradle b/library/build.gradle index 4d16b87..f8f0bbb 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -19,7 +19,7 @@ apply plugin: "com.jfrog.bintray" apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'maven' -version = "1.1.8" +version = "1.2.0" android { compileSdkVersion 23 @@ -29,7 +29,7 @@ android { minSdkVersion 11 targetSdkVersion 23 versionCode 1 - versionName "1.1.8" + versionName "1.2.0" } buildTypes { release { @@ -42,7 +42,6 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:design:23.4.0' -// compile 'org.jsoup:jsoup:+' } def siteUrl = 'https://github.com/DevLight-Mobile-Agency/NavigationTabBar' diff --git a/library/src/main/java/com/gigamole/library/BottomNavigationTabBarBehavior.java b/library/src/main/java/com/gigamole/library/BottomNavigationTabBarBehavior.java deleted file mode 100644 index 81bc4cb..0000000 --- a/library/src/main/java/com/gigamole/library/BottomNavigationTabBarBehavior.java +++ /dev/null @@ -1,406 +0,0 @@ -package com.gigamole.library; - -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.TypedArray; -import android.os.Build; -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.ViewPropertyAnimatorUpdateListener; -import android.support.v4.view.animation.LinearOutSlowInInterpolator; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.Interpolator; - -/** - * - */ -public class BottomNavigationTabBarBehavior extends VerticalScrollingBehavior { - private static final class ViewPropertyAnimatorCompat2 { - private ViewPropertyAnimatorCompat va1; - private ViewPropertyAnimatorCompat va2; - - public ViewPropertyAnimatorCompat2(NavigationTabBar ntb) { - va1 = ViewCompat.animate(ntb); - va2 = null; - View bg = ntb.getBackgroundView(); - if (bg != null) { - va2 = ViewCompat.animate(bg); - } - } - - public void setDuration(long d) { - va1.setDuration(d); - if(va2!=null) va2.setDuration(d); - } - - public void setUpdateListener(ViewPropertyAnimatorUpdateListener viewPropertyAnimatorUpdateListener) { - va1.setUpdateListener(viewPropertyAnimatorUpdateListener); - //DON'T SET listener for va2!! we just need one updatelistener! - } - - public void setInterpolator(Interpolator interpolator) { - va1.setInterpolator(interpolator); - if(va2!=null) va2.setInterpolator(interpolator); - } - - public void cancel() { - va1.cancel(); - if(va2!=null) va2.cancel(); - } - - - public void translationY_start(int offset) { - va1.translationY(offset).start(); - if(va2!=null) va2.translationY(offset).start(); - } - } - private static final class ObjectAnimator2 { - private ObjectAnimator va1; - private ObjectAnimator va2; - public ObjectAnimator2(ObjectAnimator va1,ObjectAnimator va2) { - this.va1=va1; - this.va2=va2; - } - - public void cancel() { - va1.cancel(); - if(va2!=null) va2.cancel(); - } - - public void start() { - va1.start(); - if(va2!=null) va2.start(); - } - - public void setDuration(int d) { - va1.setDuration(d); - if(va2!=null) va2.setDuration(d); - } - - public void setInterpolator(Interpolator interpolator) { - va1.setInterpolator(interpolator); - if(va2!=null) va2.setInterpolator(interpolator); - } - - public void addUpdateListener(ValueAnimator.AnimatorUpdateListener animatorUpdateListener) { - va1.addUpdateListener(animatorUpdateListener); - } - } - - private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator(); - private static final int ANIM_DURATION = 300; - - private boolean hidden = false; - private ViewPropertyAnimatorCompat2 translationAnimator; - private ObjectAnimator2 translationObjectAnimator; - private Snackbar.SnackbarLayout snackbarLayout; - private FloatingActionButton floatingActionButton; - private int mSnackbarHeight = -1; - private boolean fabBottomMarginInitialized = false; - private float targetOffset = 0, fabTargetOffset = 0, fabDefaultBottomMargin = 0, snackBarY = 0; - private boolean behaviorTranslationEnabled = true; - - /** - * Constructor - */ - public BottomNavigationTabBarBehavior() { - super(); - } - - public BottomNavigationTabBarBehavior(boolean behaviorTranslationEnabled) { - super(); - this.behaviorTranslationEnabled = behaviorTranslationEnabled; - } - - public BottomNavigationTabBarBehavior(Context context, AttributeSet attrs) { - super(context, attrs); - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavigationTabBar); - a.recycle(); - } - - @Override - public boolean onLayoutChild(CoordinatorLayout parent, NavigationTabBar child, int layoutDirection) { - boolean layoutChild = super.onLayoutChild(parent, child, layoutDirection); - return layoutChild; - } - - - @Override - public boolean onDependentViewChanged(CoordinatorLayout parent,NavigationTabBar child, View dependency) { - return super.onDependentViewChanged(parent, child, dependency); - } - - @Override - public void onDependentViewRemoved(CoordinatorLayout parent, NavigationTabBar child, View dependency) { - super.onDependentViewRemoved(parent, child, dependency); - } - - @Override - public boolean layoutDependsOn(CoordinatorLayout parent, NavigationTabBar child, View dependency) { - updateSnackbar(child, dependency); - updateFloatingActionButton(dependency); - return super.layoutDependsOn(parent, child, dependency); - } - - @Override - public void onNestedVerticalOverScroll(CoordinatorLayout coordinatorLayout, NavigationTabBar child, @ScrollDirection int direction, int currentOverScroll, int totalOverScroll) { - } - - @Override - public void onDirectionNestedPreScroll(CoordinatorLayout coordinatorLayout, NavigationTabBar child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection) { - } - - @Override - protected boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, NavigationTabBar child, View target, float velocityX, float velocityY, @ScrollDirection int scrollDirection) { - return false; - } - - @Override - public void onNestedScroll(CoordinatorLayout coordinatorLayout, NavigationTabBar 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, NavigationTabBar 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(NavigationTabBar 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 NavigationTabBar 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_start(offset); - } - } - - /** - * Manage animation for Android >= KITKAT - * - * @param child - */ - private void ensureOrCancelAnimator(NavigationTabBar child, boolean withAnimation) { - if (translationAnimator == null) { - translationAnimator = new ViewPropertyAnimatorCompat2(child); - translationAnimator.setDuration(withAnimation ? ANIM_DURATION : 0); - translationAnimator.setUpdateListener(new ViewPropertyAnimatorUpdateListener() { - @Override - public void onAnimationUpdate(View view) { - // Animate snackbar - if (snackbarLayout != null && snackbarLayout.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - targetOffset = view.getMeasuredHeight() - view.getTranslationY(); - ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) snackbarLayout.getLayoutParams(); - p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) targetOffset); - snackbarLayout.requestLayout(); - } - // Animate Floating Action Button - if (floatingActionButton != null && floatingActionButton.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) floatingActionButton.getLayoutParams(); - fabTargetOffset = fabDefaultBottomMargin - view.getTranslationY() + snackBarY; - p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) fabTargetOffset); - floatingActionButton.requestLayout(); - } - } - }); - translationAnimator.setInterpolator(INTERPOLATOR); - } else { - translationAnimator.setDuration(withAnimation ? ANIM_DURATION : 0); - translationAnimator.cancel(); - } - } - - private static ObjectAnimator objectAnimatorOfTranslationY(View target, int offset) { - ObjectAnimator res; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - res = ObjectAnimator.ofFloat(target, View.TRANSLATION_Y, offset); - } else { - res = new ObjectAnimator(); - res.setTarget(target); - res.setPropertyName("translationY"); - res.setFloatValues(offset); - } - return res; - } - - - /** - * Manage animation for Android < KITKAT - * - * @param child - */ - private void ensureOrCancelObjectAnimation(final NavigationTabBar child, final int offset, boolean withAnimation) { - - if (translationObjectAnimator != null) { - translationObjectAnimator.cancel(); - } - - ObjectAnimator ta1= objectAnimatorOfTranslationY(child,offset); - ObjectAnimator ta2=null; - View bg= child.getBackgroundView(); - if(bg!=null) { - ta2= objectAnimatorOfTranslationY(bg,offset); - } - translationObjectAnimator=new ObjectAnimator2(ta1,ta2); - 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(); - } - // Animate Floating Action Button - if (floatingActionButton != null && floatingActionButton.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) floatingActionButton.getLayoutParams(); - fabTargetOffset = fabDefaultBottomMargin - child.getTranslationY() + snackBarY; - p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) fabTargetOffset); - floatingActionButton.requestLayout(); - } - } - }); - } - - - public static BottomNavigationTabBarBehavior from(NavigationTabBar 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 BottomNavigationTabBarBehavior)) { - throw new IllegalArgumentException( - "The view is not associated with BottomNavigationTabBarBehavior"); - } - return (BottomNavigationTabBarBehavior) behavior; - } - - /** - * Enable or not the behavior translation - * @param behaviorTranslationEnabled - */ - public void setBehaviorTranslationEnabled(boolean behaviorTranslationEnabled) { - this.behaviorTranslationEnabled = behaviorTranslationEnabled; - } - - /** - * Hide AHBottomNavigation with animation - * @param view - * @param offset - */ - public void hideView(NavigationTabBar view, int offset, boolean withAnimation) { - if (!hidden) { - hidden = true; - animateOffset(view, offset, true, withAnimation); - } - } - - /** - * Reset AHBottomNavigation position with animation - * @param view - */ - public void resetOffset(NavigationTabBar 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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - snackbarLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - if (floatingActionButton != null && - floatingActionButton.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) floatingActionButton.getLayoutParams(); - snackBarY = bottom - v.getY(); - fabTargetOffset = fabDefaultBottomMargin - child.getTranslationY() + snackBarY; - p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) fabTargetOffset); - floatingActionButton.requestLayout(); - } - } - }); - } - - 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(); - } - } - } - - /** - * Update floating action button bottom margin - */ - public void updateFloatingActionButton(View dependency) { - if (dependency != null && dependency instanceof FloatingActionButton) { - floatingActionButton = (FloatingActionButton) dependency; - if (!fabBottomMarginInitialized && dependency.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - fabBottomMarginInitialized = true; - ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) dependency.getLayoutParams(); - fabDefaultBottomMargin = p.bottomMargin; - } - } - } -} \ No newline at end of file diff --git a/library/src/main/java/com/gigamole/library/VerticalScrollingBehavior.java b/library/src/main/java/com/gigamole/library/VerticalScrollingBehavior.java deleted file mode 100644 index c4bb460..0000000 --- a/library/src/main/java/com/gigamole/library/VerticalScrollingBehavior.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.gigamole.library; - - -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 on 11/22/2015. - */ -public abstract class VerticalScrollingBehavior extends CoordinatorLayout.Behavior { - - 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); - } - -} \ No newline at end of file diff --git a/library/src/main/java/com/gigamole/library/behavior/NavigationTabBarBehavior.java b/library/src/main/java/com/gigamole/library/behavior/NavigationTabBarBehavior.java new file mode 100644 index 0000000..9d2ae82 --- /dev/null +++ b/library/src/main/java/com/gigamole/library/behavior/NavigationTabBarBehavior.java @@ -0,0 +1,293 @@ +package com.gigamole.library.behavior; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Build; +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.ViewPropertyAnimatorUpdateListener; +import android.support.v4.view.animation.LinearOutSlowInInterpolator; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Interpolator; + +import com.gigamole.library.R; +import com.gigamole.library.ntb.NavigationTabBar; + +public class NavigationTabBarBehavior extends VerticalScrollingBehavior { + + private final static Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator(); + private final static int ANIMATION_DURATION = 300; + + private ViewPropertyAnimatorCompat mTranslationAnimator; + private ObjectAnimator mTranslationObjectAnimator; + private Snackbar.SnackbarLayout mSnackbarLayout; + private FloatingActionButton mFloatingActionButton; + + private int mSnackbarHeight = -1; + private float + mTargetOffset = 0, + mFabTargetOffset = 0, + mFabDefaultBottomMargin = 0; + + private boolean mHidden; + private boolean mFabBottomMarginInitialized; + private boolean mBehaviorTranslationEnabled = true; + + public NavigationTabBarBehavior(final boolean behaviorTranslationEnabled) { + super(); + this.mBehaviorTranslationEnabled = behaviorTranslationEnabled; + } + + public NavigationTabBarBehavior(final Context context, final AttributeSet attrs) { + super(context, attrs); + final TypedArray typedArray = + context.obtainStyledAttributes(attrs, R.styleable.NavigationTabBar); + typedArray.recycle(); + } + + @Override + public boolean onLayoutChild(CoordinatorLayout parent, NavigationTabBar child, int layoutDirection) { + return super.onLayoutChild(parent, child, layoutDirection); + } + + + @Override + public boolean onDependentViewChanged(CoordinatorLayout parent, NavigationTabBar child, View dependency) { + return super.onDependentViewChanged(parent, child, dependency); + } + + @Override + public void onDependentViewRemoved(CoordinatorLayout parent, NavigationTabBar child, View dependency) { + super.onDependentViewRemoved(parent, child, dependency); + } + + @Override + public boolean layoutDependsOn(CoordinatorLayout parent, NavigationTabBar child, View dependency) { + updateSnackbar(child, dependency); + updateFloatingActionButton(child, dependency); + return super.layoutDependsOn(parent, child, dependency); + } + + @Override + public void onNestedVerticalOverScroll(CoordinatorLayout coordinatorLayout, NavigationTabBar child, @ScrollDirection int direction, int currentOverScroll, int totalOverScroll) { + } + + @Override + public void onDirectionNestedPreScroll(CoordinatorLayout coordinatorLayout, NavigationTabBar child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection) { + } + + @Override + protected boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, NavigationTabBar child, View target, float velocityX, float velocityY, @ScrollDirection int scrollDirection) { + return false; + } + + @Override + public void onNestedScroll(CoordinatorLayout coordinatorLayout, NavigationTabBar 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, NavigationTabBar child, View directTargetChild, View target, int nestedScrollAxes) { + return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); + } + + // Handle scroll direction + private void handleDirection(NavigationTabBar child, int scrollDirection) { + if (!mBehaviorTranslationEnabled) return; + if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_DOWN && mHidden) { + mHidden = false; + animateOffset(child, 0, false, true); + } else if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_UP && !mHidden) { + mHidden = true; + animateOffset(child, child.getHeight(), false, true); + } + } + + // Animate offset + private void animateOffset(final NavigationTabBar child, final int offset, boolean forceAnimation, boolean withAnimation) { + if (!mBehaviorTranslationEnabled && !forceAnimation) return; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + ensureOrCancelObjectAnimation(child, offset, withAnimation); + mTranslationObjectAnimator.start(); + } else { + ensureOrCancelAnimator(child, withAnimation); + mTranslationAnimator.translationY(offset).start(); + } + } + + // Manage animation for Android >= KITKAT + private void ensureOrCancelAnimator(final NavigationTabBar child, boolean withAnimation) { + if (mTranslationAnimator == null) { + mTranslationAnimator = ViewCompat.animate(child); + mTranslationAnimator.setDuration(withAnimation ? ANIMATION_DURATION : 0); + mTranslationAnimator.setUpdateListener(new ViewPropertyAnimatorUpdateListener() { + @Override + public void onAnimationUpdate(View view) { + // Animate snackbar + if (mSnackbarLayout != null && mSnackbarLayout.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + mTargetOffset = child.getBarHeight() - view.getTranslationY(); + + final ViewGroup.MarginLayoutParams p = + (ViewGroup.MarginLayoutParams) mSnackbarLayout.getLayoutParams(); + + p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) mTargetOffset); + mSnackbarLayout.requestLayout(); + } + // Animate Floating Action Button + if (mFloatingActionButton != null && mFloatingActionButton.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + final ViewGroup.MarginLayoutParams p = + (ViewGroup.MarginLayoutParams) mFloatingActionButton.getLayoutParams(); + + mFabTargetOffset = mFabDefaultBottomMargin - view.getTranslationY(); + p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) mFabTargetOffset); + mFloatingActionButton.requestLayout(); + } + } + }); + mTranslationAnimator.setInterpolator(INTERPOLATOR); + } else { + mTranslationAnimator.setDuration(withAnimation ? ANIMATION_DURATION : 0); + mTranslationAnimator.cancel(); + } + } + + private static ObjectAnimator objectAnimatorOfTranslationY(View target, int offset) { + final ObjectAnimator res; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) + res = ObjectAnimator.ofFloat(target, View.TRANSLATION_Y, offset); + else { + res = new ObjectAnimator(); + res.setTarget(target); + res.setPropertyName("translationY"); + res.setFloatValues(offset); + } + return res; + } + + // Manage animation for Android < KITKAT + private void ensureOrCancelObjectAnimation(final NavigationTabBar child, final int offset, boolean withAnimation) { + if (mTranslationObjectAnimator != null) mTranslationObjectAnimator.cancel(); + + mTranslationObjectAnimator = objectAnimatorOfTranslationY(child, offset); + mTranslationObjectAnimator.setDuration(withAnimation ? ANIMATION_DURATION : 0); + mTranslationObjectAnimator.setInterpolator(INTERPOLATOR); + mTranslationObjectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (mSnackbarLayout != null && mSnackbarLayout.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + mTargetOffset = child.getBarHeight() - child.getTranslationY(); + + final ViewGroup.MarginLayoutParams p = + (ViewGroup.MarginLayoutParams) mSnackbarLayout.getLayoutParams(); + p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) mTargetOffset); + mSnackbarLayout.requestLayout(); + } + // Animate Floating Action Button + if (mFloatingActionButton != null && mFloatingActionButton.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + mFabTargetOffset = mFabDefaultBottomMargin - child.getTranslationY(); + + final ViewGroup.MarginLayoutParams p = + (ViewGroup.MarginLayoutParams) mFloatingActionButton.getLayoutParams(); + p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) mFabTargetOffset); + mFloatingActionButton.requestLayout(); + } + } + }); + } + + public static NavigationTabBarBehavior from(NavigationTabBar view) { + final ViewGroup.LayoutParams params = view.getLayoutParams(); + if (!(params instanceof CoordinatorLayout.LayoutParams)) + throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); + + final CoordinatorLayout.Behavior behavior = + ((CoordinatorLayout.LayoutParams) params).getBehavior(); + if (!(behavior instanceof NavigationTabBarBehavior)) + throw new IllegalArgumentException( + "The view is not associated with NavigationTabBarBehavior"); + + return (NavigationTabBarBehavior) behavior; + } + + // Enable or not the behavior translation + public void setBehaviorTranslationEnabled(boolean behaviorTranslationEnabled) { + this.mBehaviorTranslationEnabled = behaviorTranslationEnabled; + } + + // Hide NTB with animation + public void hideView(NavigationTabBar view, int offset, boolean withAnimation) { + if (!mHidden) { + mHidden = true; + animateOffset(view, offset, true, withAnimation); + } + } + + // Reset NTB position with animation + public void resetOffset(NavigationTabBar view, boolean withAnimation) { + if (mHidden) { + mHidden = false; + animateOffset(view, 0, true, withAnimation); + } + } + + // Update Snackbar bottom margin + public void updateSnackbar(final NavigationTabBar child, View dependency) { + if (dependency != null && dependency instanceof Snackbar.SnackbarLayout) { + mSnackbarLayout = (Snackbar.SnackbarLayout) dependency; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + mSnackbarLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + if (mFloatingActionButton != null && + mFloatingActionButton.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + mFabTargetOffset = mFabDefaultBottomMargin - child.getTranslationY(); + + final ViewGroup.MarginLayoutParams p = + (ViewGroup.MarginLayoutParams) mFloatingActionButton.getLayoutParams(); + p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, (int) mFabTargetOffset); + mFloatingActionButton.requestLayout(); + } + } + }); + } + + if (mSnackbarHeight == -1) mSnackbarHeight = dependency.getHeight(); + final int targetMargin = (int) (child.getBarHeight() - child.getTranslationY()); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) child.bringToFront(); + if (dependency.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + final ViewGroup.MarginLayoutParams p = + (ViewGroup.MarginLayoutParams) dependency.getLayoutParams(); + + p.setMargins(p.leftMargin, p.topMargin, p.rightMargin, targetMargin); + dependency.requestLayout(); + } + } + } + + // Update floating action button bottom margin + public void updateFloatingActionButton(final NavigationTabBar child, final View dependency) { + if (dependency != null && dependency instanceof FloatingActionButton) { + mFloatingActionButton = (FloatingActionButton) dependency; + + if (!mFabBottomMarginInitialized && + dependency.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + mFabBottomMarginInitialized = true; + + final ViewGroup.MarginLayoutParams p = + (ViewGroup.MarginLayoutParams) dependency.getLayoutParams(); + mFabDefaultBottomMargin = p.bottomMargin; + } + } + } +} \ No newline at end of file diff --git a/library/src/main/java/com/gigamole/library/behavior/VerticalScrollingBehavior.java b/library/src/main/java/com/gigamole/library/behavior/VerticalScrollingBehavior.java new file mode 100644 index 0000000..3c0223e --- /dev/null +++ b/library/src/main/java/com/gigamole/library/behavior/VerticalScrollingBehavior.java @@ -0,0 +1,128 @@ +package com.gigamole.library.behavior; + + +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; + +public abstract class VerticalScrollingBehavior extends CoordinatorLayout.Behavior { + + private int mTotalDyUnconsumed = 0; + private int mTotalDy = 0; + + @ScrollDirection + private int mOverScrollDirection = ScrollDirection.SCROLL_NONE; + @ScrollDirection + private int mScrollDirection = ScrollDirection.SCROLL_NONE; + + @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; + } + + public VerticalScrollingBehavior() { + super(); + } + + public VerticalScrollingBehavior(final Context context, final AttributeSet attrs) { + super(context, attrs); + } + + @ScrollDirection + public int getOverScrollDirection() { + return mOverScrollDirection; + } + + @ScrollDirection + public int getScrollDirection() { + return mScrollDirection; + } + + // Direction of the overscroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN + // Unconsumed value, negative or positive based on the direction + // Cumulative value for current direction + public abstract void onNestedVerticalOverScroll(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int direction, int currentOverScroll, int totalOverScroll); + + // 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); + } + +} \ No newline at end of file diff --git a/library/src/main/java/com/gigamole/library/NavigationTabBar.java b/library/src/main/java/com/gigamole/library/ntb/NavigationTabBar.java similarity index 87% rename from library/src/main/java/com/gigamole/library/NavigationTabBar.java rename to library/src/main/java/com/gigamole/library/ntb/NavigationTabBar.java index ea9148e..553884f 100644 --- a/library/src/main/java/com/gigamole/library/NavigationTabBar.java +++ b/library/src/main/java/com/gigamole/library/ntb/NavigationTabBar.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.gigamole.library; +package com.gigamole.library.ntb; import android.animation.Animator; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; @@ -53,6 +54,9 @@ import android.view.animation.LinearInterpolator; import android.widget.Scroller; +import com.gigamole.library.R; +import com.gigamole.library.behavior.NavigationTabBarBehavior; + import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; @@ -63,7 +67,7 @@ */ public class NavigationTabBar extends View implements ViewPager.OnPageChangeListener { - // NTP constants + // NTB constants private final static String PREVIEW_BADGE = "0"; private final static String PREVIEW_TITLE = "Title"; private final static int INVALID_INDEX = -1; @@ -74,53 +78,47 @@ public class NavigationTabBar extends View implements ViewPager.OnPageChangeList private final static int DEFAULT_INACTIVE_COLOR = Color.parseColor("#9f90af"); private final static int DEFAULT_ACTIVE_COLOR = Color.WHITE; - private final static float MIN_FRACTION = 0.0f; - private final static float NON_SCALED_FRACTION = 0.35f; - private final static float MAX_FRACTION = 1.0f; + private final static float MIN_FRACTION = 0.0F; + private final static float NON_SCALED_FRACTION = 0.35F; + private final static float MAX_FRACTION = 1.0F; private final static int MIN_ALPHA = 0; private final static int MAX_ALPHA = 255; - private final static float ACTIVE_ICON_SCALE_BY = 0.3f; - private final static float ICON_SIZE_FRACTION = 0.45f; - - private final static float TITLE_ACTIVE_ICON_SCALE_BY = 0.2f; - private final static float TITLE_ICON_SIZE_FRACTION = 0.45f; - private final static float TITLE_ACTIVE_SCALE_BY = 0.2f; - private final static float TITLE_SIZE_FRACTION = 0.2f; - private final static float TITLE_MARGIN_FRACTION = 0.15f; - private final static float TITLE_MARGIN_SCALE_FRACTION = 0.25f; - - private final static float BADGE_HORIZONTAL_FRACTION = 0.5f; - private final static float BADGE_VERTICAL_FRACTION = 0.75f; - private final static float BADGE_TITLE_SIZE_FRACTION = 0.9f; + private final static float ACTIVE_ICON_SCALE_BY = 0.3F; + private final static float ICON_SIZE_FRACTION = 0.45F; - private final static int ALL_INDEX = 0; - private final static int ACTIVE_INDEX = 1; + private final static float TITLE_ACTIVE_ICON_SCALE_BY = 0.2F; + private final static float TITLE_ICON_SIZE_FRACTION = 0.45F; + private final static float TITLE_ACTIVE_SCALE_BY = 0.2F; + private final static float TITLE_SIZE_FRACTION = 0.2F; + private final static float TITLE_MARGIN_FRACTION = 0.15F; + private final static float TITLE_MARGIN_SCALE_FRACTION = 0.25F; - private final static int LEFT_INDEX = 0; - private final static int CENTER_INDEX = 1; - private final static int RIGHT_INDEX = 2; + private final static float BADGE_HORIZONTAL_FRACTION = 0.5F; + private final static float BADGE_VERTICAL_FRACTION = 0.75F; + private final static float BADGE_TITLE_SIZE_FRACTION = 0.9F; - private final static int TOP_INDEX = 0; - private final static int BOTTOM_INDEX = 1; - - private final static float LEFT_FRACTION = 0.25f; - private final static float CENTER_FRACTION = 0.5f; - private final static float RIGHT_FRACTION = 0.75f; + public final static float LEFT_FRACTION = 0.25F; + public final static float CENTER_FRACTION = 0.5F; + public final static float RIGHT_FRACTION = 0.75F; private final static Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator(); private final static Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator(); + private final static Interpolator OUT_SLOW_IN_INTERPOLATOR = new LinearOutSlowInInterpolator(); - // NTP and pointer bounds + // NTB and pointer bounds private final RectF mBounds = new RectF(); private final RectF mPointerBounds = new RectF(); // Badge bounds and bg badge bounds private final Rect mBadgeBounds = new Rect(); private final RectF mBgBadgeBounds = new RectF(); - //external backgroundView for the tab layout - private View backgroundView=null; + // NTB background + private Bitmap mBackground; + // Check whether need to reload background + private boolean mNeedInvalidateBackground = true; + // Canvas, where all of other canvas will be merged private Bitmap mBitmap; private final Canvas mCanvas = new Canvas(); @@ -137,13 +135,18 @@ public class NavigationTabBar extends View implements ViewPager.OnPageChangeList private Bitmap mPointerBitmap; private final Canvas mPointerCanvas = new Canvas(); - private int navBarHeight; //updated in OnMeasure() - private boolean isBehaviorTranslationSet = false; - private boolean behaviorTranslationEnabled = false; - private boolean needHideBottomNavigation = false; - private boolean hideBottomNavigationWithAnimation = false; - private BottomNavigationTabBarBehavior bottomNavigationBehavior; + // External background view for the NTB +// private View mBackgroundView = null; + private NavigationTabBarBehavior mBehavior; + // Detect if behavior already set + private boolean mIsBehaviorSet; + // Detect if behavior enabled + private boolean mBehaviorEnabled; + // Detect if need to hide NTB + private boolean mNeedHide; + // Detect if need animate animate or force hide + private boolean mAnimateHide; // Main paint private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG) { @@ -204,7 +207,7 @@ public class NavigationTabBar extends View implements ViewPager.OnPageChangeList private final ResizeInterpolator mResizeInterpolator = new ResizeInterpolator(); private int mAnimationDuration; - // NTP models + // NTB models private final List mModels = new ArrayList<>(); // Variables for ViewPager @@ -312,12 +315,20 @@ public NavigationTabBar(final Context context, final AttributeSet attrs, final i typedArray.getBoolean(R.styleable.NavigationTabBar_ntb_badge_use_typeface, false) ); - setTitleMode(typedArray.getInt(R.styleable.NavigationTabBar_ntb_title_mode, ALL_INDEX)); + setTitleMode( + typedArray.getInt( + R.styleable.NavigationTabBar_ntb_title_mode, TitleMode.ALL_INDEX + ) + ); setBadgePosition( - typedArray.getInt(R.styleable.NavigationTabBar_ntb_badge_position, RIGHT_INDEX) + typedArray.getInt( + R.styleable.NavigationTabBar_ntb_badge_position, BadgePosition.RIGHT_INDEX + ) ); setBadgeGravity( - typedArray.getInt(R.styleable.NavigationTabBar_ntb_badge_gravity, TOP_INDEX) + typedArray.getInt( + R.styleable.NavigationTabBar_ntb_badge_gravity, BadgeGravity.TOP_INDEX + ) ); setBadgeBgColor(typedArray.getColor(R.styleable.NavigationTabBar_ntb_badge_bg_color, 0)); setBadgeTitleColor(typedArray.getColor(R.styleable.NavigationTabBar_ntb_badge_title_color, 0)); @@ -339,7 +350,7 @@ public NavigationTabBar(final Context context, final AttributeSet attrs, final i ) ); setCornersRadius( - typedArray.getDimension(R.styleable.NavigationTabBar_ntb_corners_radius, 0.0f) + typedArray.getDimension(R.styleable.NavigationTabBar_ntb_corners_radius, 0.0F) ); // Init animator @@ -463,10 +474,10 @@ public TitleMode getTitleMode() { private void setTitleMode(final int index) { switch (index) { - case ACTIVE_INDEX: + case TitleMode.ACTIVE_INDEX: setTitleMode(TitleMode.ACTIVE); break; - case ALL_INDEX: + case TitleMode.ALL_INDEX: default: setTitleMode(TitleMode.ALL); } @@ -483,13 +494,13 @@ public BadgePosition getBadgePosition() { private void setBadgePosition(final int index) { switch (index) { - case LEFT_INDEX: + case BadgePosition.LEFT_INDEX: setBadgePosition(BadgePosition.LEFT); break; - case CENTER_INDEX: + case BadgePosition.CENTER_INDEX: setBadgePosition(BadgePosition.CENTER); break; - case RIGHT_INDEX: + case BadgePosition.RIGHT_INDEX: default: setBadgePosition(BadgePosition.RIGHT); } @@ -506,10 +517,10 @@ public BadgeGravity getBadgeGravity() { private void setBadgeGravity(final int index) { switch (index) { - case BOTTOM_INDEX: + case BadgeGravity.BOTTOM_INDEX: setBadgeGravity(BadgeGravity.BOTTOM); break; - case TOP_INDEX: + case BadgeGravity.TOP_INDEX: default: setBadgeGravity(BadgeGravity.TOP); } @@ -660,7 +671,8 @@ public void setViewPager(final ViewPager viewPager) { } if (mViewPager == viewPager) return; - if (mViewPager != null) mViewPager.setOnPageChangeListener(null); + if (mViewPager != null) //noinspection deprecation + mViewPager.setOnPageChangeListener(null); if (viewPager.getAdapter() == null) throw new IllegalStateException("ViewPager does not provide adapter instance."); @@ -697,6 +709,28 @@ public void setOnPageChangeListener(final ViewPager.OnPageChangeListener listene mOnPageChangeListener = listener; } + // Return if the behavior translation is enabled + public boolean isBehaviorEnabled() { + return mBehaviorEnabled; + } + + // Set the behavior translation value + public void setBehaviorEnabled(final boolean enabled) { + mBehaviorEnabled = enabled; + + if (getParent() != null && getParent() instanceof CoordinatorLayout) { + final ViewGroup.LayoutParams params = getLayoutParams(); + if (mBehavior == null) mBehavior = new NavigationTabBarBehavior(enabled); + else mBehavior.setBehaviorTranslationEnabled(enabled); + + ((CoordinatorLayout.LayoutParams) params).setBehavior(mBehavior); + if (mNeedHide) { + mNeedHide = false; + mBehavior.hideView(this, (int) getBarHeight(), mAnimateHide); + } + } + } + public int getModelIndex() { return mIndex; } @@ -759,7 +793,7 @@ private void updateIndicatorPosition(final float fraction) { postInvalidate(); } - // Update NTP + // Update NTB private void notifyDataSetChanged() { requestLayout(); postInvalidate(); @@ -811,15 +845,14 @@ public boolean onTouchEvent(final MotionEvent event) { return true; } + @SuppressLint("DrawAllocation") @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - // Get measure size final int width = MeasureSpec.getSize(widthMeasureSpec); final int height = MeasureSpec.getSize(heightMeasureSpec); - navBarHeight=height; if (mModels.isEmpty() || width == 0 || height == 0) return; @@ -845,12 +878,14 @@ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec final Rect badgeBounds = new Rect(); mBadgePaint.setTextSize(mBadgeTitleSize); mBadgePaint.getTextBounds(PREVIEW_BADGE, 0, 1, badgeBounds); - mBadgeMargin = (badgeBounds.height() * 0.5f) + + mBadgeMargin = (badgeBounds.height() * 0.5F) + (mBadgeTitleSize * BADGE_HORIZONTAL_FRACTION * BADGE_VERTICAL_FRACTION); } } else { + // Disable vertical translation in coordinator layout + mBehaviorEnabled = false; + // Disable other features mIsHorizontalOrientation = false; - behaviorTranslationEnabled=false; //disable vertical translation in coordinator layout mIsTitled = false; mIsBadged = false; @@ -859,7 +894,7 @@ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec } // Set bounds for NTB - mBounds.set(0.0f, 0.0f, width, height - mBadgeMargin); + mBounds.set(0.0F, 0.0F, width, height - mBadgeMargin); // Set main bitmap mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); @@ -915,16 +950,56 @@ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec updateIndicatorPosition(MAX_FRACTION); } - if (!isBehaviorTranslationSet) { - //The translation behavior has to be set up after the super.onMeasure has been called. - setBehaviorTranslationEnabled(behaviorTranslationEnabled); - isBehaviorTranslationSet = true; + //The translation behavior has to be set up after the super.onMeasure has been called + if (!mIsBehaviorSet) { + setBehaviorEnabled(mBehaviorEnabled); + mIsBehaviorSet = true; } + if (mBackground == null || mNeedInvalidateBackground) { + if (getBackground() != null) { + if (getBackground() instanceof BitmapDrawable) + mBackground = ((BitmapDrawable) getBackground()).getBitmap(); + else { + mBackground = Bitmap.createBitmap( + (int) mBounds.width(), (int) mBounds.height(), Bitmap.Config.ARGB_8888 + ); + + final Canvas backgroundCanvas = new Canvas(mBackground); + getBackground().setBounds( + 0, 0, backgroundCanvas.getWidth(), backgroundCanvas.getHeight() + ); + getBackground().draw(backgroundCanvas); + } + + setBackgroundDrawable(null); + mNeedInvalidateBackground = false; + } + } + } + + @Override + public void setBackgroundColor(final int color) { + mNeedInvalidateBackground = true; + super.setBackgroundColor(color); } + @SuppressWarnings("deprecation") + @Override + public void setBackgroundDrawable(final Drawable background) { + mNeedInvalidateBackground = true; + super.setBackgroundDrawable(background); + } + + @SuppressWarnings("ConstantConditions") @Override protected void onDraw(final Canvas canvas) { + if (mBackground != null) + canvas.drawBitmap( + mBackground, 0.0F, + mBadgeGravity == BadgeGravity.TOP ? getBadgeMargin() : 0.0F, null + ); + if (mCanvas == null || mPointerCanvas == null || mIconsCanvas == null || mTitlesCanvas == null) return; @@ -936,7 +1011,7 @@ protected void onDraw(final Canvas canvas) { if (mIsTitled) mTitlesCanvas.drawColor(0, PorterDuff.Mode.CLEAR); // Get pointer badge margin for gravity - final float pointerBadgeMargin = mBadgeGravity == BadgeGravity.TOP ? mBadgeMargin : 0.0f; + final float pointerBadgeMargin = mBadgeGravity == BadgeGravity.TOP ? mBadgeMargin : 0.0F; // Draw our model colors for (int i = 0; i < mModels.size(); i++) { @@ -951,7 +1026,7 @@ protected void onDraw(final Canvas canvas) { } else { final float top = mModelSize * i; final float bottom = top + mModelSize; - mCanvas.drawRect(0.0f, top, mBounds.width(), bottom, mPaint); + mCanvas.drawRect(0.0F, top, mBounds.width(), bottom, mPaint); } } @@ -961,14 +1036,14 @@ protected void onDraw(final Canvas canvas) { mPointerLeftTop, pointerBadgeMargin, mPointerRightBottom, mBounds.height() + pointerBadgeMargin ); - else mPointerBounds.set(0.0f, mPointerLeftTop, mBounds.width(), mPointerRightBottom); + else mPointerBounds.set(0.0F, mPointerLeftTop, mBounds.width(), mPointerRightBottom); // Draw pointer for model colors if (mCornersRadius == 0) mPointerCanvas.drawRect(mPointerBounds, mPaint); else mPointerCanvas.drawRoundRect(mPointerBounds, mCornersRadius, mCornersRadius, mPaint); // Draw pointer into main canvas - mCanvas.drawBitmap(mPointerBitmap, 0.0f, 0.0f, mPointerPaint); + mCanvas.drawBitmap(mPointerBitmap, 0.0F, 0.0F, mPointerPaint); // Set vars for icon when model with title or without final float iconMarginTitleHeight = mIconSize + mTitleMargin + mModelTitleSize; @@ -984,20 +1059,20 @@ protected void onDraw(final Canvas canvas) { final float matrixCenterY; // Set offset to titles - final float leftTitleOffset = (mModelSize * i) + (mModelSize * 0.5f); + final float leftTitleOffset = (mModelSize * i) + (mModelSize * 0.5F); final float topTitleOffset = - mBounds.height() - (mBounds.height() - iconMarginTitleHeight) * 0.5f; + mBounds.height() - (mBounds.height() - iconMarginTitleHeight) * 0.5F; if (mIsHorizontalOrientation) { - leftOffset = (mModelSize * i) + (mModelSize - model.mIcon.getWidth()) * 0.5f; - topOffset = (mBounds.height() - model.mIcon.getHeight()) * 0.5f; + leftOffset = (mModelSize * i) + (mModelSize - model.mIcon.getWidth()) * 0.5F; + topOffset = (mBounds.height() - model.mIcon.getHeight()) * 0.5F; } else { - leftOffset = (mBounds.width() - (float) model.mIcon.getWidth()) * 0.5f; - topOffset = (mModelSize * i) + (mModelSize - (float) model.mIcon.getHeight()) * 0.5f; + leftOffset = (mBounds.width() - (float) model.mIcon.getWidth()) * 0.5F; + topOffset = (mModelSize * i) + (mModelSize - (float) model.mIcon.getHeight()) * 0.5F; } - matrixCenterX = leftOffset + (float) model.mIcon.getWidth() * 0.5f; - matrixCenterY = topOffset + (float) model.mIcon.getHeight() * 0.5f; + matrixCenterX = leftOffset + (float) model.mIcon.getWidth() * 0.5F; + matrixCenterY = topOffset + (float) model.mIcon.getHeight() * 0.5F; // Title translate position final float titleTranslate = @@ -1031,7 +1106,7 @@ protected void onDraw(final Canvas canvas) { mIconPaint.setAlpha(MAX_ALPHA); if (model.mSelectedIcon != null) mSelectedIconPaint.setAlpha(MAX_ALPHA); - // Check if we handle models from touch on NTP or from ViewPager + // Check if we handle models from touch on NTB or from ViewPager // There is a strange logic // of ViewPager onPageScrolled method, so it is if (mIsSetIndexFromTabBar) { @@ -1110,11 +1185,11 @@ else if (i == mIndex) } // Draw general bitmap - canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null); + canvas.drawBitmap(mBitmap, 0.0F, 0.0F, null); // Draw icons bitmap on top - canvas.drawBitmap(mIconsBitmap, 0.0f, pointerBadgeMargin, null); + canvas.drawBitmap(mIconsBitmap, 0.0F, pointerBadgeMargin, null); // Draw titles bitmap on top - if (mIsTitled) canvas.drawBitmap(mTitlesBitmap, 0.0f, pointerBadgeMargin, null); + if (mIsTitled) canvas.drawBitmap(mTitlesBitmap, 0.0F, pointerBadgeMargin, null); // If is not badged, exit if (!mIsBadged) return; @@ -1123,7 +1198,7 @@ else if (i == mIndex) final float modelBadgeMargin = mBadgeGravity == BadgeGravity.TOP ? mBadgeMargin : mBounds.height(); final float modelBadgeOffset = - mBadgeGravity == BadgeGravity.TOP ? 0.0f : mBounds.height() - mBadgeMargin; + mBadgeGravity == BadgeGravity.TOP ? 0.0F : mBounds.height() - mBadgeMargin; for (int i = 0; i < mModels.size(); i++) { final Model model = mModels.get(i); @@ -1147,18 +1222,20 @@ else if (i == mIndex) (mModelSize * i) + (mModelSize * mBadgePosition.mPositionFraction); // If is badge title only one char, so create circle else round rect + final float badgeMargin = mBadgeMargin * model.mBadgeFraction; if (model.getBadgeTitle().length() == 1) { - final float badgeMargin = mBadgeMargin * model.mBadgeFraction; mBgBadgeBounds.set( badgeBoundsHorizontalOffset - badgeMargin, modelBadgeMargin - badgeMargin, badgeBoundsHorizontalOffset + badgeMargin, modelBadgeMargin + badgeMargin ); } else mBgBadgeBounds.set( - badgeBoundsHorizontalOffset - mBadgeBounds.centerX() - horizontalPadding, - modelBadgeMargin - (mBadgeMargin * model.mBadgeFraction), - badgeBoundsHorizontalOffset + mBadgeBounds.centerX() + horizontalPadding, - modelBadgeOffset + (verticalPadding * 2.0f) + mBadgeBounds.height() + badgeBoundsHorizontalOffset - + Math.max(badgeMargin, mBadgeBounds.centerX() + horizontalPadding), + modelBadgeMargin - badgeMargin, + badgeBoundsHorizontalOffset + + Math.max(badgeMargin, mBadgeBounds.centerX() + horizontalPadding), + modelBadgeOffset + (verticalPadding * 2.0F) + mBadgeBounds.height() ); // Set color and alpha for badge bg @@ -1167,17 +1244,18 @@ else if (i == mIndex) mBadgePaint.setAlpha((int) (MAX_ALPHA * model.mBadgeFraction)); // Set corners to round rect for badge bg and draw - final float cornerRadius = mBgBadgeBounds.height() * 0.5f; + final float cornerRadius = mBgBadgeBounds.height() * 0.5F; canvas.drawRoundRect(mBgBadgeBounds, cornerRadius, cornerRadius, mBadgePaint); // Set color and alpha for badge title if (model.mBadgeFraction == MIN_FRACTION) mBadgePaint.setColor(Color.TRANSPARENT); - else mBadgePaint.setColor(mBadgeTitleColor == 0 ? model.getColor() : mBadgeTitleColor); + else //noinspection ResourceAsColor + mBadgePaint.setColor(mBadgeTitleColor == 0 ? model.getColor() : mBadgeTitleColor); mBadgePaint.setAlpha((int) (MAX_ALPHA * model.mBadgeFraction)); // Set badge title center position and draw title - final float badgeHalfHeight = mBadgeBounds.height() * 0.5f; - float badgeVerticalOffset = (mBgBadgeBounds.height() * 0.5f) + badgeHalfHeight - + final float badgeHalfHeight = mBadgeBounds.height() * 0.5F; + float badgeVerticalOffset = (mBgBadgeBounds.height() * 0.5F) + badgeHalfHeight - mBadgeBounds.bottom + modelBadgeOffset; canvas.drawText( model.getBadgeTitle(), badgeBoundsHorizontalOffset, badgeVerticalOffset + @@ -1318,7 +1396,7 @@ private void updateInactiveModel( matrixCenterX, matrixCenterY ); - mModelTitlePaint.setTextSize(mModelTitleSize * (mIsScaled ? 1.0f : textScale)); + mModelTitlePaint.setTextSize(mModelTitleSize * (mIsScaled ? 1.0F : textScale)); if (mTitleMode == TitleMode.ACTIVE) mModelTitlePaint.setAlpha(MIN_ALPHA); // Reset icons alpha @@ -1408,29 +1486,9 @@ public Parcelable onSaveInstanceState() { return savedState; } - /** - * set the background view for the tab bar - * Also make sure that the background view height match the tab bar height - * you should call this like this: - * - * @param bg - */ - public void setBackgroundView(final View bg) { - post(new Runnable() { - @Override - public void run() { - bg.getLayoutParams().height = (int) getBarHeight(); - bg.requestLayout(); - backgroundView=bg; - } - }); - } - public View getBackgroundView() { - return backgroundView; - } - private static class SavedState extends BaseSavedState { - int index; + + private int index; public SavedState(Parcelable superState) { super(superState); @@ -1483,6 +1541,50 @@ private float clampValue(final float value, final float max, final float min) { return Math.max(Math.min(value, min), max); } + // Hide NTB with animation + public void hide() { + hide(true); + } + + // Hide NTB with or without animation + public void hide(final boolean withAnimation) { + if (mBehavior != null) mBehavior.hideView(this, (int) getBarHeight(), withAnimation); + else if (getParent() != null && getParent() instanceof CoordinatorLayout) { + mNeedHide = true; + mAnimateHide = withAnimation; + } else scrollDown(withAnimation); + } + + // Show NTB with animation + public void show() { + show(true); + } + + // Show NTB with or without animation + public void show(final boolean withAnimation) { + if (mBehavior != null) + mBehavior.resetOffset(this, withAnimation); + else scrollUp(withAnimation); + } + + // Hide NTB or bg on scroll down + private void scrollDown(final boolean withAnimation) { + ViewCompat.animate(this) + .translationY(getBarHeight()) + .setInterpolator(new LinearOutSlowInInterpolator()) + .setDuration(withAnimation ? DEFAULT_ANIMATION_DURATION : 0) + .start(); + } + + // Show NTB or bg on scroll up + private void scrollUp(final boolean withAnimation) { + ViewCompat.animate(this) + .translationY(0.0F) + .setInterpolator(OUT_SLOW_IN_INTERPOLATOR) + .setDuration(withAnimation ? DEFAULT_ANIMATION_DURATION : 0) + .start(); + } + // Model class public static class Model { @@ -1708,14 +1810,14 @@ public void startScroll(int startX, int startY, int dx, int dy) { private class ResizeInterpolator implements Interpolator { // Spring factor - private final float mFactor = 1.0f; + private final static float FACTOR = 1.0F; // Check whether side we move private boolean mResizeIn; @Override public float getInterpolation(final float input) { - if (mResizeIn) return (float) (1.0f - Math.pow((1.0f - input), 2.0f * mFactor)); - else return (float) (Math.pow(input, 2.0f * mFactor)); + if (mResizeIn) return (float) (1.0F - Math.pow((1.0F - input), 2.0F * FACTOR)); + else return (float) (Math.pow(input, 2.0F * FACTOR)); } public float getResizeInterpolation(final float input, final boolean resizeIn) { @@ -1726,7 +1828,11 @@ public float getResizeInterpolation(final float input, final boolean resizeIn) { // Model title mode public enum TitleMode { - ALL, ACTIVE + + ALL, ACTIVE; + + public final static int ALL_INDEX = 0; + public final static int ACTIVE_INDEX = 1; } // Model badge position @@ -1734,6 +1840,10 @@ public enum BadgePosition { LEFT(LEFT_FRACTION), CENTER(CENTER_FRACTION), RIGHT(RIGHT_FRACTION); + public final static int LEFT_INDEX = 0; + public final static int CENTER_INDEX = 1; + public final static int RIGHT_INDEX = 2; + private float mPositionFraction; BadgePosition() { @@ -1747,7 +1857,11 @@ public enum BadgePosition { // Model badge gravity public enum BadgeGravity { - TOP, BOTTOM + + TOP, BOTTOM; + + public final static int TOP_INDEX = 0; + public final static int BOTTOM_INDEX = 1; } // Out listener for selected index @@ -1756,105 +1870,4 @@ public interface OnTabBarSelectedIndexListener { void onEndTabSelected(final Model model, final int index); } - - - /** - * Return if the behavior translation is enabled - * - * @return a boolean value - */ - public boolean isBehaviorTranslationEnabled() { - return behaviorTranslationEnabled; - } - /** - * Set the behavior translation value - * - * @param behaviorTranslationEnabled boolean for the state - */ - public void setBehaviorTranslationEnabled(boolean behaviorTranslationEnabled) { - this.behaviorTranslationEnabled = behaviorTranslationEnabled; - if (getParent() instanceof CoordinatorLayout) { - ViewGroup.LayoutParams params = getLayoutParams(); - if (bottomNavigationBehavior == null) { - bottomNavigationBehavior = new BottomNavigationTabBarBehavior(behaviorTranslationEnabled); - } else { - bottomNavigationBehavior.setBehaviorTranslationEnabled(behaviorTranslationEnabled); - } - ((CoordinatorLayout.LayoutParams) params).setBehavior(bottomNavigationBehavior); - if (needHideBottomNavigation) { - needHideBottomNavigation = false; - bottomNavigationBehavior.hideView(this, navBarHeight, hideBottomNavigationWithAnimation); - } - } - } - - /** - * Hide Bottom Navigation with animation - */ - public void hideBottomNavigation() { - hideBottomNavigation(true); - } - - /** - * Hide Bottom Navigation with or without animation - * - * @param withAnimation Boolean - */ - public void hideBottomNavigation(boolean withAnimation) { - if (bottomNavigationBehavior != null) { - bottomNavigationBehavior.hideView(this, navBarHeight, withAnimation); - } else if (getParent() instanceof CoordinatorLayout) { - needHideBottomNavigation = true; - hideBottomNavigationWithAnimation = withAnimation; - } else { - scrollDownView(this,withAnimation); - if(backgroundView!=null) { - scrollDownView(backgroundView,withAnimation); - } - } - } - - - /** - * Restore Bottom Navigation with animation - */ - public void restoreBottomNavigation() { - restoreBottomNavigation(true); - } - - /** - * Restore Bottom Navigation with or without animation - * - * @param withAnimation Boolean - */ - public void restoreBottomNavigation(boolean withAnimation) { - if (bottomNavigationBehavior != null) { - bottomNavigationBehavior.resetOffset(this, withAnimation); - } else { - // Show bottom navigation - scrollUpView(this,withAnimation); - if(backgroundView!=null) { - scrollUpView(backgroundView,withAnimation); - } - } - } - - private void scrollDownView(View view,boolean withAnimation) { - int HIDE_ANIM_DURATION=300; - // Hide bottom navigation - ViewCompat.animate(view) - .translationY(navBarHeight) - .setInterpolator(new LinearOutSlowInInterpolator()) - .setDuration(withAnimation ? HIDE_ANIM_DURATION : 0) - .start(); - } - private void scrollUpView(View view,boolean withAnimation) { - int SHOW_ANIM_DURATION=300; - ViewCompat.animate(view) - .translationY(0) - .setInterpolator(new LinearOutSlowInInterpolator()) - .setDuration(withAnimation ? SHOW_ANIM_DURATION : 0) - .start(); - } - }