diff --git a/README.md b/README.md
index b6a35f6..86fdb58 100644
--- a/README.md
+++ b/README.md
@@ -1,141 +1,222 @@
-[](http://android-arsenal.com/details/1/3382)
+
+
+
+
+
+
+
NavigationTabBar
-===================
+================
Navigation tab bar with colorful interactions.
-Horizontal NTB|NTB bottom, badge, all title |NTB top, badge, typeface, active title|Vertical NTB|NTB Samples|
-:-------------------------:|:-------------------------:|:-------------------------:|:-------------------------:|:-------------------------:
-||||
-
-U can check the sample app [here](https://github.com/DevLight-Mobile-Agency/NavigationTabBar/tree/master/app).
+[](http://android-arsenal.com/details/1/3382)
+
+[](https://github.com/DevLight-Mobile-Agency)
+
+[](https://bintray.com/gigamole/maven/navigationtabbar/_latestVersion)
+
+[](https://github.com/DevLight-Mobile-Agency/NavigationTabBar/blob/master/LICENSE.txt)
+
+[](https://www.codacy.com/app/gigamole53/NavigationTabBar?utm_source=github.com&utm_medium=referral&utm_content=DevLight-Mobile-Agency/NavigationTabBar&utm_campaign=Badge_Grade)
+
+
+
+You can check the sample app [here](https://github.com/DevLight-Mobile-Agency/NavigationTabBar/tree/master/app).
+
+Warn
+====
+```
+This library is not more supported.
+If you want to add new feature or fix a bug, grab source code and do it.
+If you think your fix or feature would be useful to other developers,
+I can add link of your repository to this README file.
+Thank you for using our libraries.
+```
Download
-------------
-
-You can download a .aar` from GitHub's [releases page](https://github.com/DevLight-Mobile-Agency/NavigationTabBar/releases).
-
-Or use Gradle jCenter:
-
-```groovy
-dependencies {
- repositories {
- mavenCentral()
- maven {
- url 'http://dl.bintray.com/gigamole/maven/'
- }
- }
- compile 'com.github.devlight.navigationtabbar:library:+'
-}
-```
+========
-Or Gradle Maven Central:
+You can download a `.aar` from GitHub's [releases page](https://github.com/DevLight-Mobile-Agency/NavigationTabBar/releases).
+You can use Gradle:
```groovy
-compile 'com.github.devlight.navigationtabbar:library:1.1.1'
+compile 'devlight.io:navigationtabbar:1.2.5'
```
Or Maven:
-
```groovy
- com.github.devlight.navigationtabbar
- library
- 1.1.1
+ devlight.io
+ navigationtabbar
+ 1.2.5
aar
```
+Or Ivy:
+```groovy
+
+
+
+```
+
Android SDK Version
-=========
+===================
-NavigationTabBar requires a minimum sdk version of 11.
+`NavigationTabBar` requires a minimum SDK version of 11.
Sample
-========
+======
+
+Parameters
-For NTB you can set such parameters as:
+For `NTB` you can set such parameters as:
- - models:
+ - models:
+ allows you to set `NTB` models, where you set icon and color. Can be set up only via code.
- allows you to set NTB models, where you set icon 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.
+ - view pager:
+ allows you to connect `NTB` with `ViewPager`. If you want your can also set `OnPageChangeListener`.
+
+ - background color:
+ allows you to set background to `NTB` which automatically set with offset relative to badge gravity and corners radius.
+
+ - model selected icon:
+ allows you to set selected icon when current model is active.
- - model title:
+ - model title:
+ allows you to enable title in you model.
- allows you to enable title in you model.
+ - model badge:
+ allows you to enable badge in you model.
+
+ - use custom typeface on badge:
+ allows you to handle set of custom typeface in your badge.
- - model badge:
+ - title mode:
+ allows you to handle mode of the model title show. Can show all or only active.
- allows you to enable badge in you model.
+ - title size:
+ allows you to set titles size.
- - use custom typeface on badge:
+ - scale mode:
+ allows you to handle mode of the model icon and title scale.
- allows you to handle set of custom typeface in your badge.
-
- - title mode:
-
- allows you to handle mode of the model title show. Can show all or only active.
+ - tint mode:
+ allows you to enable or disable icon tinting.
+
+ - badge size:
+ allows you to set badges size.
- - badge position:
-
- allows you to set the badge position in you model. Can be: left(25%), center(50%) and right(75%).
+ - badge position:
+ allows you to set the badge position in you model. Can be: left(25%), center(50%) and right(75%).
- - badge gravity:
-
- allows you to set the badge gravity in NTB. Can be top or bottom.
+ - badge gravity:
+ allows you to set the badge gravity in NTB. Can be top or bottom.
+
+ - badge colors:
+ allows you to set the badge bg and title colors.
- - typeface:
-
- allows you to set custom typeface to your title.
+ - typeface:
+ allows you to set custom typeface to your title.
- - corners radius:
-
- allows you to set corners radius of pointer.
+ - corners radius:
+ allows you to set corners radius of pointer.
- - animation duration:
-
- allows you to set animation duration.
-
- - inactive color:
+ - icon size fraction:
+ allows you to set icon size fraction relative to smaller model side.
- allows you to set inactive icon color.
+ - animation duration:
+ allows you to set animation duration.
+
+ - inactive color:
+ allows you to set inactive icon color.
- - active color:
-
- allows you to set active icon color.
+ - active color:
+ allows you to set active icon color.
- - tab bar listener:
-
- allows you to set listener which triggering on start or on end when you set model index.
+ - tab bar listener:
+ allows you to set listener which triggering on start or on end when you set model index.
- - preview colors:
-
- allows you to set preview colors, which generate count of models equals to count of colors.
+ - preview colors:
+ allows you to set preview colors, which generate count of models equals to count of colors.
+
+Tips
-Orientation automatically detected according to view size.
+Creation of models occurs through `Builder` pattern.
+`ModelBuilder` requires two fields: icon and color. Title, badge title and selected icon is optional.
-If your set ViewPager you can action down on active pointer and do like drag.
+You can set selected icon. Resize and scale of selected icon equals to original icon.
+Orientation automatically detected according to `View` size.
+
+By default badge bg color is the active model color and badge title color is the model bg color. To reset colors just set `AUTO_COLOR` value to badge bg and title color.
+By default badge sizes and title sizes is auto fit. To reset calculation just set `AUTO_SIZE` value to badge size and title size.
+By default icon size fraction is `0.5F` (half of smaller side of `NTB` model). To reset scale fraction of icon to automatic just put in method `AUTO_SCALE` value.
+
+If your set `ViewPager` and enable swipe you can action down on active pointer and do like drag.
+
+Init
Check out in code init:
```java
final NavigationTabBar navigationTabBar = (NavigationTabBar) findViewById(R.id.ntb);
final ArrayList models = new ArrayList<>();
-models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_first), colors[0]));
-models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_second), colors[1]));
-models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_third), colors[2]));
-models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fourth), colors[3]));
-models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fifth), colors[4]));
+models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_first),
+ Color.parseColor(colors[0])
+ ).title("Heart")
+ .badgeTitle("NTB")
+ .build()
+);
+models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_second),
+ Color.parseColor(colors[1])
+ ).title("Cup")
+ .badgeTitle("with")
+ .build()
+);
+models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_third),
+ Color.parseColor(colors[2])
+ ).title("Diploma")
+ .badgeTitle("state")
+ .build()
+);
+models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fourth),
+ Color.parseColor(colors[3])
+ ).title("Flag")
+ .badgeTitle("icon")
+ .build()
+);
+models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fifth),
+ Color.parseColor(colors[4])
+ ).title("Medal")
+ .badgeTitle("777")
+ .build()
+);
navigationTabBar.setModels(models);
navigationTabBar.setViewPager(viewPager, 2);
@@ -145,24 +226,18 @@ navigationTabBar.setBadgePosition(NavigationTabBar.BadgePosition.CENTER);
navigationTabBar.setTypeface("fonts/custom_font.ttf");
navigationTabBar.setIsBadged(true);
navigationTabBar.setIsTitled(true);
+navigationTabBar.setIsTinted(true);
navigationTabBar.setIsBadgeUseTypeface(true);
-```
-
-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();
- }
-});
+navigationTabBar.setBadgeBgColor(Color.RED);
+navigationTabBar.setBadgeTitleColor(Color.WHITE);
+navigationTabBar.setIsSwiped(true);
+navigationTabBar.setBgColor(Color.BLACK);
+navigationTabBar.setBadgeSize(10);
+navigationTabBar.setTitleSize(10);
+navigationTabBar.setIconSizeFraction(0.5);
```
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();
@@ -170,13 +245,23 @@ model.showBadge();
model.toggleBadge();
model.updateBadgeTitle("Here some title like NEW or some integer value");
```
-
+
+To enable behavior translation inside `CoordinatorLayout` when at bottom of screen:
+```java
+navigationTabBar.setBehaviorEnabled(true);
+```
+
+To deselect active index and reset pointer:
+```java
+navigationTabBar.deselect();
+```
+
Other methods check out in sample.
And XML init:
```xml
-
+ app:ntb_badge_use_typeface="true"
+ app:ntb_swiped="true"
+ app:ntb_bg_color="#000"
+ app:ntb_icon_size_fraction="http://github.com/0.5"
+ app:ntb_badge_size="10sp"
+ app:ntb_title_size="10sp"/>
+```
+
+XML属性中文详解:
+```xml
+
```
Getting Help
-======
+============
To report a specific problem or feature request, [open a new issue on Github](https://github.com/DevLight-Mobile-Agency/NavigationTabBar/issues/new).
-License
-======
+Xamarin
+=======
-Apache 2.0 and MIT. See [LICENSE](https://github.com/DevLight-Mobile-Agency/NavigationTabBar/blob/master/LICENSE.txt) file for details.
+Thanks to [Martijn van Dijk](https://github.com/martijn00) for developing Xamarin bindings library for [NavigationTabBar](https://github.com/martijn00/NavigationTabBarXamarin).
+Plugin is available on [Nuget](https://www.nuget.org/packages/Xam.Plugins.Android.NavigationTabBar/).
-Inspiration
-======
+## use navbar using materialize css
+navbar using materialize css is really easy and would take just assigning right classes to the html tags and it would create a navigation tab bar using its prewritten css and js files. This can be easily used in html pages using downloaded files or cdn links...
+https://materializecss.com/navbar.html
+
+Credits
+=======
+
+Sincere thanks, to portal [FAnDroid.info](http://www.fandroid.info) [(StartAndroid)](https://www.youtube.com/channel/UCzE7HcbvyEiS5ea1rVRbPLQ) who released the review of this library in detail. If you understand the Russian language, then feel free to see the video or read the [text version](http://www.fandroid.info/obzor-biblioteki-navigationtabbar-dlya-android-ot-komandy-devlight/) of its great post.
+
+
+
+
+
-Tapbar interactions| Circle interactions | Title interactions
-:-------------------------:|:-------------------------:|:-------------------------:
-||
+Inspired by:
+
+|[Sergey Valiukh](https://dribbble.com/SergeyValiukh)|
+|:--------------------------------------------------:|
+|[](https://dribbble.com/shots/2071319-GIF-of-the-Tapbar-Interactions)|
Thanks to [Valery Nuzhniy](https://www.pinterest.com/hevbolt/) for NTB badge design.
Author
+======
+
+Created by [Basil Miller](https://github.com/GIGAMOLE) - [@gigamole](mailto:gigamole53@gmail.com)
+
+Company
=======
-Made in [DevLight Mobile Agency](https://github.com/DevLight-Mobile-Agency)
+[](https://www.facebook.com/devlightagency) [](https://twitter.com/DevLightIO) [](https://www.linkedin.com/company/devlight)
+
+[Here](https://github.com/DevLight-Mobile-Agency) you can see open source work developed by Devlight LLC.
+This and another works is an exclusive property of Devlight LLC.
+
+If you want to use this library in applications which will be available on Google Play, please report us about it or author of the library.
+
+Whether you're searching for a new partner or trusted team for creating your new great product we are always ready to start work with you.
+
+You can contact us via info@devlight.io or opensource@devlight.io.
+Thanks in advance.
-Created by [Basil Miller](https://github.com/GIGAMOLE) - [@gigamole](mailto:http://gigamole53@gmail.com)
\ No newline at end of file
+Devlight LLC, 2016
+[devlight.io](http://devlight.io)
diff --git a/app/build.gradle b/app/build.gradle
index a3466fc..146f017 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 23
- buildToolsVersion "23.0.2"
+ compileSdkVersion 27
+ buildToolsVersion '27.0.3'
defaultConfig {
- applicationId "com.gigamole.navigationtabbar"
- minSdkVersion 11
- targetSdkVersion 23
+ applicationId "devlight.io.navigationtabbar"
+ minSdkVersion 14
+ targetSdkVersion 27
versionCode 1
versionName "1.0"
}
@@ -17,10 +17,18 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+ compileOptions {
+ targetCompatibility 1.7
+ sourceCompatibility 1.7
+ }
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile project(':library')
- compile 'com.android.support:appcompat-v7:23.2.1'
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation project(':navigationtabbar')
+// compile 'com.github.devlight.navigationtabbar:navigationtabbar:1.2.3'
+
+ implementation 'com.android.support:appcompat-v7:27.1.1'
+ implementation 'com.android.support:design:27.1.1'
+ implementation 'com.android.support:support-v4:27.1.1'
}
diff --git a/library/src/androidTest/java/com/gigamole/library/ApplicationTest.java b/app/src/androidTest/java/devlight/io/sample/ApplicationTest.java
similarity index 91%
rename from library/src/androidTest/java/com/gigamole/library/ApplicationTest.java
rename to app/src/androidTest/java/devlight/io/sample/ApplicationTest.java
index 35e36d7..3d59832 100644
--- a/library/src/androidTest/java/com/gigamole/library/ApplicationTest.java
+++ b/app/src/androidTest/java/devlight/io/sample/ApplicationTest.java
@@ -1,4 +1,4 @@
-package com.gigamole.library;
+package devlight.io.sample;
import android.app.Application;
import android.test.ApplicationTestCase;
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f18db43..68997ed 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
+
@@ -17,9 +18,15 @@
-
+
+
+
+
+
+
diff --git a/app/src/main/java/com/gigamole/navigationtabbar/SamplesNtbActivity.java b/app/src/main/java/com/gigamole/navigationtabbar/SamplesNtbActivity.java
deleted file mode 100644
index ac9a6c0..0000000
--- a/app/src/main/java/com/gigamole/navigationtabbar/SamplesNtbActivity.java
+++ /dev/null
@@ -1,133 +0,0 @@
-package com.gigamole.navigationtabbar;
-
-import android.app.Activity;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.support.v4.graphics.ColorUtils;
-import android.widget.Toast;
-
-import com.gigamole.library.NavigationTabBar;
-
-import java.util.ArrayList;
-
-/**
- * Created by GIGAMOLE on 28.03.2016.
- */
-public class SamplesNtbActivity extends Activity {
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_samples_ntb);
- initUI();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- System.runFinalization();
- Runtime.getRuntime().gc();
- System.gc();
- }
-
- private void initUI() {
- final NavigationTabBar ntbSample1 = (NavigationTabBar) findViewById(R.id.ntb_sample_1);
- final ArrayList models1 = new ArrayList<>();
- models1.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_first), Color.WHITE));
- models1.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_second), Color.LTGRAY));
- models1.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_third), Color.GRAY));
- models1.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fourth), Color.DKGRAY));
- ntbSample1.setModels(models1);
-
- final NavigationTabBar ntbSample2 = (NavigationTabBar) findViewById(R.id.ntb_sample_2);
- final ArrayList models2 = new ArrayList<>();
- models2.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_seventh), Color.YELLOW));
- models2.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_sixth), Color.YELLOW));
- models2.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fifth), Color.YELLOW));
- models2.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_eighth), Color.YELLOW));
- models2.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_second), Color.YELLOW));
- ntbSample2.setModels(models2);
- ntbSample2.setModelIndex(3, true);
-
- final NavigationTabBar ntbSample3 = (NavigationTabBar) findViewById(R.id.ntb_sample_3);
- final ArrayList models3 = new ArrayList<>();
- models3.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_seventh), Color.RED));
- models3.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_seventh), Color.RED));
- models3.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_seventh), Color.RED));
- ntbSample3.setModels(models3);
- ntbSample3.setModelIndex(1, true);
-
- final NavigationTabBar ntbSample4 = (NavigationTabBar) findViewById(R.id.ntb_sample_4);
- final int bgColor = Color.parseColor("#423752");
- final ArrayList models4 = new ArrayList<>();
- models4.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fifth), bgColor));
- models4.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_first), bgColor));
- models4.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fourth), bgColor));
- models4.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_sixth), bgColor));
- models4.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_third), bgColor));
- ntbSample4.setModels(models4);
- ntbSample4.setModelIndex(2, true);
-
- final NavigationTabBar ntbSample5 = (NavigationTabBar) findViewById(R.id.ntb_sample_5);
- final ArrayList models5 = new ArrayList<>();
- models5.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fifth), Color.WHITE));
- models5.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_first), Color.WHITE));
- models5.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fourth), Color.WHITE));
- models5.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_sixth), Color.WHITE));
- ntbSample5.setModels(models5);
- ntbSample5.setModelIndex(2, true);
- ntbSample5.setOnTabBarSelectedIndexListener(new NavigationTabBar.OnTabBarSelectedIndexListener() {
- @Override
- public void onStartTabSelected(final NavigationTabBar.Model model, final int index) {
-
- }
-
- @Override
- public void onEndTabSelected(final NavigationTabBar.Model model, final int index) {
- Toast.makeText(SamplesNtbActivity.this, String.format("onEndTabSelected #%d", index), Toast.LENGTH_SHORT).show();
- }
- });
-
- final NavigationTabBar ntbSample6 = (NavigationTabBar) findViewById(R.id.ntb_sample_6);
- final ArrayList models6 = new ArrayList<>();
- models6.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fifth), randomColor()));
- models6.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_first), randomColor()));
- models6.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fourth), randomColor()));
- models6.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_sixth), randomColor()));
- ntbSample6.setModels(models6);
- }
-
- private int randomColor(){
- float[] TEMP_HSL = new float[]{0, 0, 0};
- float[] hsl = TEMP_HSL;
- hsl[0] = (float) (Math.random() * 360);
- hsl[1] = (float) (40 + (Math.random() * 60));
- hsl[2] = (float) (40 + (Math.random() * 60));
- return ColorUtils.HSLToColor(hsl);
- }
-}
diff --git a/app/src/main/java/com/gigamole/navigationtabbar/VerticalNtbActivity.java b/app/src/main/java/com/gigamole/navigationtabbar/VerticalNtbActivity.java
deleted file mode 100644
index 6e3b7c3..0000000
--- a/app/src/main/java/com/gigamole/navigationtabbar/VerticalNtbActivity.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package com.gigamole.navigationtabbar;
-
-import android.app.Activity;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.gigamole.library.NavigationTabBar;
-
-import java.util.ArrayList;
-
-/**
- * Created by GIGAMOLE on 28.03.2016.
- */
-public class VerticalNtbActivity extends Activity {
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_vertical_ntb);
- 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_vertical_ntb);
- viewPager.setAdapter(new PagerAdapter() {
- @Override
- public int getCount() {
- return 8;
- }
-
- @Override
- public boolean isViewFromObject(final View view, final Object object) {
- return view.equals(object);
- }
-
- @Override
- public void destroyItem(final View container, final int position, final Object object) {
- ((ViewPager) container).removeView((View) object);
- }
-
- @Override
- public Object instantiateItem(final ViewGroup container, final int position) {
- final View view = LayoutInflater.from(
- getBaseContext()).inflate(R.layout.vp_item, null, false);
-
- final TextView txtPage = (TextView) view.findViewById(R.id.txt_vp_item_page);
- txtPage.setText(String.format("Page #%d", position));
-
- container.addView(view);
- return view;
- }
- });
-
- final String[] colors = getResources().getStringArray(R.array.vertical_ntb);
-
- final NavigationTabBar navigationTabBar = (NavigationTabBar) findViewById(R.id.ntb_vertical);
- final ArrayList models = new ArrayList<>();
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_first), Color.parseColor(colors[0])));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_second), Color.parseColor(colors[1])));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_third), Color.parseColor(colors[2])));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fourth), Color.parseColor(colors[3])));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fifth), Color.parseColor(colors[4])));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_sixth), Color.parseColor(colors[5])));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_seventh), Color.parseColor(colors[6])));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_eighth), Color.parseColor(colors[7])));
-
- navigationTabBar.setModels(models);
- navigationTabBar.setViewPager(viewPager, 4);
- }
-}
diff --git a/app/src/main/java/devlight/io/sample/HorizontalCoordinatorNtbActivity.java b/app/src/main/java/devlight/io/sample/HorizontalCoordinatorNtbActivity.java
new file mode 100644
index 0000000..50fbc83
--- /dev/null
+++ b/app/src/main/java/devlight/io/sample/HorizontalCoordinatorNtbActivity.java
@@ -0,0 +1,209 @@
+package devlight.io.sample;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.design.widget.CollapsingToolbarLayout;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.design.widget.Snackbar;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import devlight.io.library.ntb.NavigationTabBar;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+/**
+ * Created by GIGAMOLE on 28.03.2016.
+ */
+public class HorizontalCoordinatorNtbActivity extends Activity {
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_horizontal_coordinator_ntb);
+ initUI();
+ }
+
+ private void initUI() {
+ final ViewPager viewPager = (ViewPager) findViewById(R.id.vp_horizontal_ntb);
+ viewPager.setAdapter(new PagerAdapter() {
+ @Override
+ public int getCount() {
+ return 5;
+ }
+
+ @Override
+ public boolean isViewFromObject(final View view, final Object object) {
+ return view.equals(object);
+ }
+
+ @Override
+ public void destroyItem(final View container, final int position, final Object object) {
+ ((ViewPager) container).removeView((View) object);
+ }
+
+ @Override
+ public Object instantiateItem(final ViewGroup container, final int position) {
+ final View view = LayoutInflater.from(
+ getBaseContext()).inflate(R.layout.item_vp_list, null, false);
+
+ final RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.rv);
+ recyclerView.setHasFixedSize(true);
+ recyclerView.setLayoutManager(new LinearLayoutManager(
+ getBaseContext(), LinearLayoutManager.VERTICAL, false
+ )
+ );
+ recyclerView.setAdapter(new RecycleAdapter());
+
+ container.addView(view);
+ return view;
+ }
+ });
+
+ final String[] colors = getResources().getStringArray(R.array.default_preview);
+
+ final NavigationTabBar navigationTabBar = (NavigationTabBar) findViewById(R.id.ntb_horizontal);
+ final ArrayList models = new ArrayList<>();
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_first),
+ Color.parseColor(colors[0]))
+ .title("Heart")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_second),
+ Color.parseColor(colors[1]))
+ .title("Cup")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_third),
+ Color.parseColor(colors[2]))
+ .title("Diploma")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fourth),
+ Color.parseColor(colors[3]))
+ .title("Flag")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fifth),
+ Color.parseColor(colors[4]))
+ .title("Medal")
+ .build()
+ );
+
+ navigationTabBar.setModels(models);
+ navigationTabBar.setViewPager(viewPager, 2);
+
+ //IMPORTANT: ENABLE SCROLL BEHAVIOUR IN COORDINATOR LAYOUT
+ navigationTabBar.setBehaviorEnabled(true);
+
+ navigationTabBar.setOnTabBarSelectedIndexListener(new NavigationTabBar.OnTabBarSelectedIndexListener() {
+ @Override
+ public void onStartTabSelected(final NavigationTabBar.Model model, final int index) {
+ }
+
+ @Override
+ public void onEndTabSelected(final NavigationTabBar.Model model, final int index) {
+ model.hideBadge();
+ }
+ });
+ navigationTabBar.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(final int position) {
+
+ }
+
+ @Override
+ public void onPageScrollStateChanged(final int state) {
+
+ }
+ });
+
+ final CoordinatorLayout coordinatorLayout = (CoordinatorLayout) findViewById(R.id.parent);
+ findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ for (int i = 0; i < navigationTabBar.getModels().size(); i++) {
+ final NavigationTabBar.Model model = navigationTabBar.getModels().get(i);
+ navigationTabBar.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ final String title = String.valueOf(new Random().nextInt(15));
+ if (!model.isBadgeShowed()) {
+ model.setBadgeTitle(title);
+ model.showBadge();
+ } else model.updateBadgeTitle(title);
+ }
+ }, i * 100);
+ }
+
+ coordinatorLayout.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ final Snackbar snackbar = Snackbar.make(navigationTabBar, "Coordinator NTB", Snackbar.LENGTH_SHORT);
+ snackbar.getView().setBackgroundColor(Color.parseColor("#9b92b3"));
+ ((TextView) snackbar.getView().findViewById(R.id.snackbar_text))
+ .setTextColor(Color.parseColor("#423752"));
+ snackbar.show();
+ }
+ }, 1000);
+ }
+ });
+
+ final CollapsingToolbarLayout collapsingToolbarLayout =
+ (CollapsingToolbarLayout) findViewById(R.id.toolbar);
+ collapsingToolbarLayout.setExpandedTitleColor(Color.parseColor("#009F90AF"));
+ collapsingToolbarLayout.setCollapsedTitleTextColor(Color.parseColor("#9f90af"));
+ }
+
+ public class RecycleAdapter extends RecyclerView.Adapter {
+
+ @Override
+ public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
+ final View view = LayoutInflater.from(getBaseContext()).inflate(R.layout.item_list, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, final int position) {
+ holder.txt.setText(String.format("Navigation Item #%d", position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return 20;
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+
+ public TextView txt;
+
+ public ViewHolder(final View itemView) {
+ super(itemView);
+ txt = (TextView) itemView.findViewById(R.id.txt_vp_item_list);
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/gigamole/navigationtabbar/HorizontalNtbActivity.java b/app/src/main/java/devlight/io/sample/HorizontalNtbActivity.java
similarity index 51%
rename from app/src/main/java/com/gigamole/navigationtabbar/HorizontalNtbActivity.java
rename to app/src/main/java/devlight/io/sample/HorizontalNtbActivity.java
index 5bdc2ea..5d72919 100644
--- a/app/src/main/java/com/gigamole/navigationtabbar/HorizontalNtbActivity.java
+++ b/app/src/main/java/devlight/io/sample/HorizontalNtbActivity.java
@@ -1,4 +1,4 @@
-package com.gigamole.navigationtabbar;
+package devlight.io.sample;
import android.app.Activity;
import android.graphics.Color;
@@ -10,7 +10,7 @@
import android.view.ViewGroup;
import android.widget.TextView;
-import com.gigamole.library.NavigationTabBar;
+import devlight.io.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() {
@@ -55,7 +47,7 @@ public void destroyItem(final View container, final int position, final Object o
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
final View view = LayoutInflater.from(
- getBaseContext()).inflate(R.layout.vp_item, null, false);
+ getBaseContext()).inflate(R.layout.item_vp, null, false);
final TextView txtPage = (TextView) view.findViewById(R.id.txt_vp_item_page);
txtPage.setText(String.format("Page #%d", position));
@@ -69,37 +61,68 @@ public Object instantiateItem(final ViewGroup container, final int position) {
final NavigationTabBar navigationTabBar = (NavigationTabBar) findViewById(R.id.ntb_horizontal);
final ArrayList models = new ArrayList<>();
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_first), Color.parseColor(colors[0]), "Heart"));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_second), Color.parseColor(colors[1]), "Cup"));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_third), Color.parseColor(colors[2]), "Diploma"));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fourth), Color.parseColor(colors[3]), "Flag"));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fifth), Color.parseColor(colors[4]), "Medal"));
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_first),
+ Color.parseColor(colors[0]))
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_sixth))
+ .title("Heart")
+ .badgeTitle("NTB")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_second),
+ Color.parseColor(colors[1]))
+// .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .title("Cup")
+ .badgeTitle("with")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_third),
+ Color.parseColor(colors[2]))
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_seventh))
+ .title("Diploma")
+ .badgeTitle("state")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fourth),
+ Color.parseColor(colors[3]))
+// .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .title("Flag")
+ .badgeTitle("icon")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fifth),
+ Color.parseColor(colors[4]))
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .title("Medal")
+ .badgeTitle("777")
+ .build()
+ );
+
navigationTabBar.setModels(models);
navigationTabBar.setViewPager(viewPager, 2);
-
- navigationTabBar.setOnTabBarSelectedIndexListener(new NavigationTabBar.OnTabBarSelectedIndexListener() {
+ navigationTabBar.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
- public void onStartTabSelected(final NavigationTabBar.Model model, final int index) {
+ public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
}
@Override
- public void onEndTabSelected(final NavigationTabBar.Model model, final int index) {
- model.hideBadge();
+ public void onPageSelected(final int position) {
+ navigationTabBar.getModels().get(position).hideBadge();
}
- });
- 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();
+ public void onPageScrollStateChanged(final int state) {
+
}
});
@@ -108,23 +131,6 @@ public void run() {
public void run() {
for (int i = 0; i < navigationTabBar.getModels().size(); i++) {
final NavigationTabBar.Model model = navigationTabBar.getModels().get(i);
- switch (i) {
- case 0:
- model.setBadgeTitle("NTB");
- break;
- case 1:
- model.setBadgeTitle("with");
- break;
- case 2:
- model.setBadgeTitle("title");
- break;
- case 3:
- model.setBadgeTitle("badge");
- break;
- case 4:
- model.setBadgeTitle("777");
- break;
- }
navigationTabBar.postDelayed(new Runnable() {
@Override
public void run() {
diff --git a/app/src/main/java/com/gigamole/navigationtabbar/MainActivity.java b/app/src/main/java/devlight/io/sample/MainActivity.java
similarity index 85%
rename from app/src/main/java/com/gigamole/navigationtabbar/MainActivity.java
rename to app/src/main/java/devlight/io/sample/MainActivity.java
index d549f35..927295d 100644
--- a/app/src/main/java/com/gigamole/navigationtabbar/MainActivity.java
+++ b/app/src/main/java/devlight/io/sample/MainActivity.java
@@ -1,4 +1,4 @@
-package com.gigamole.navigationtabbar;
+package devlight.io.sample;
import android.app.Activity;
import android.content.Intent;
@@ -19,6 +19,8 @@ protected void onCreate(Bundle savedInstanceState) {
private void initUI() {
final View btnHorizontalNtb = findViewById(R.id.btn_horizontal_ntb);
btnHorizontalNtb.setOnClickListener(this);
+ final View btnHorizontalCoordinatorNtb = findViewById(R.id.btn_horizontal_coordinator_ntb);
+ btnHorizontalCoordinatorNtb.setOnClickListener(this);
final View btnTopHorizontalNtb = findViewById(R.id.btn_horizontal_top_ntb);
btnTopHorizontalNtb.setOnClickListener(this);
final View btnVerticalNtb = findViewById(R.id.btn_vertical_ntb);
@@ -53,6 +55,11 @@ public void onAnimationEnd(final View view) {
new Intent(MainActivity.this, TopHorizontalNtbActivity.class)
);
break;
+ case R.id.btn_horizontal_coordinator_ntb:
+ startActivity(
+ new Intent(MainActivity.this, HorizontalCoordinatorNtbActivity.class)
+ );
+ break;
case R.id.btn_vertical_ntb:
startActivity(
new Intent(MainActivity.this, VerticalNtbActivity.class)
@@ -63,6 +70,8 @@ public void onAnimationEnd(final View view) {
new Intent(MainActivity.this, SamplesNtbActivity.class)
);
break;
+ default:
+ break;
}
}
diff --git a/app/src/main/java/devlight/io/sample/SamplesNtbActivity.java b/app/src/main/java/devlight/io/sample/SamplesNtbActivity.java
new file mode 100644
index 0000000..9e16a66
--- /dev/null
+++ b/app/src/main/java/devlight/io/sample/SamplesNtbActivity.java
@@ -0,0 +1,200 @@
+package devlight.io.sample;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v4.graphics.ColorUtils;
+import android.widget.Toast;
+
+import devlight.io.library.ntb.NavigationTabBar;
+
+import java.util.ArrayList;
+
+/**
+ * Created by GIGAMOLE on 28.03.2016.
+ */
+public class SamplesNtbActivity extends Activity {
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_samples_ntb);
+ initUI();
+ }
+
+ private void initUI() {
+ final NavigationTabBar ntbSample1 = (NavigationTabBar) findViewById(R.id.ntb_sample_1);
+ final ArrayList models1 = new ArrayList<>();
+ models1.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_first), Color.WHITE
+ ).build()
+ );
+ models1.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_second), Color.LTGRAY
+ ).build()
+ );
+ models1.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_third), Color.GRAY
+ ).build()
+ );
+ models1.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fourth), Color.DKGRAY
+ ).build()
+ );
+ ntbSample1.setModels(models1);
+
+ final NavigationTabBar ntbSample2 = (NavigationTabBar) findViewById(R.id.ntb_sample_2);
+ final ArrayList models2 = new ArrayList<>();
+ models2.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_seventh), Color.YELLOW
+ ).build()
+ );
+ models2.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_sixth), Color.YELLOW
+ ).build()
+ );
+ models2.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fifth), Color.YELLOW
+ ).build()
+ );
+ models2.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_eighth), Color.YELLOW
+ ).build()
+ );
+ models2.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_second), Color.YELLOW
+ ).build()
+ );
+ ntbSample2.setModels(models2);
+ ntbSample2.setModelIndex(3, true);
+
+ final NavigationTabBar ntbSample3 = (NavigationTabBar) findViewById(R.id.ntb_sample_3);
+ final ArrayList models3 = new ArrayList<>();
+ models3.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_seventh), Color.RED
+ ).build()
+ );
+ models3.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_seventh), Color.RED
+ ).build()
+ );
+ models3.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_seventh), Color.RED
+ ).build()
+ );
+ ntbSample3.setModels(models3);
+ ntbSample3.setModelIndex(1, true);
+
+ final NavigationTabBar ntbSample4 = (NavigationTabBar) findViewById(R.id.ntb_sample_4);
+ final int bgColor = Color.parseColor("#423752");
+ final ArrayList models4 = new ArrayList<>();
+ models4.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fifth), bgColor
+ ).build()
+ );
+ models4.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_first), bgColor
+ ).build()
+ );
+ models4.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fourth), bgColor
+ ).build()
+ );
+ models4.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_sixth), bgColor
+ ).build()
+ );
+ models4.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_third), bgColor
+ ).build()
+ );
+ ntbSample4.setModels(models4);
+ ntbSample4.setModelIndex(2, true);
+
+ final NavigationTabBar ntbSample5 = (NavigationTabBar) findViewById(R.id.ntb_sample_5);
+ final ArrayList models5 = new ArrayList<>();
+ models5.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fifth), Color.WHITE
+ ).build()
+ );
+ models5.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_first), Color.WHITE
+ ).build()
+ );
+ models5.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fourth), Color.WHITE
+ ).build()
+ );
+ models5.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_sixth), Color.WHITE
+ ).build()
+ );
+ ntbSample5.setModels(models5);
+ ntbSample5.setModelIndex(2, true);
+ ntbSample5.setOnTabBarSelectedIndexListener(new NavigationTabBar.OnTabBarSelectedIndexListener() {
+ @Override
+ public void onStartTabSelected(final NavigationTabBar.Model model, final int index) {
+
+ }
+
+ @Override
+ public void onEndTabSelected(final NavigationTabBar.Model model, final int index) {
+ Toast.makeText(SamplesNtbActivity.this, String.format("onEndTabSelected #%d", index), Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ final NavigationTabBar ntbSample6 = (NavigationTabBar) findViewById(R.id.ntb_sample_6);
+ final ArrayList models6 = new ArrayList<>();
+ models6.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fifth), randomColor()
+ ).build()
+ );
+ models6.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_first), randomColor()
+ ).build()
+ );
+ models6.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fourth), randomColor()
+ ).build()
+ );
+ models6.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_sixth), randomColor()
+ ).build()
+ );
+ ntbSample6.setModels(models6);
+ }
+
+ private int randomColor() {
+ float[] TEMP_HSL = new float[]{0, 0, 0};
+ float[] hsl = TEMP_HSL;
+ hsl[0] = (float) (Math.random() * 360);
+ hsl[1] = (float) (40 + (Math.random() * 60));
+ hsl[2] = (float) (40 + (Math.random() * 60));
+ return ColorUtils.HSLToColor(hsl);
+ }
+}
diff --git a/app/src/main/java/com/gigamole/navigationtabbar/TopHorizontalNtbActivity.java b/app/src/main/java/devlight/io/sample/TopHorizontalNtbActivity.java
similarity index 50%
rename from app/src/main/java/com/gigamole/navigationtabbar/TopHorizontalNtbActivity.java
rename to app/src/main/java/devlight/io/sample/TopHorizontalNtbActivity.java
index 1834bc2..c12ca21 100644
--- a/app/src/main/java/com/gigamole/navigationtabbar/TopHorizontalNtbActivity.java
+++ b/app/src/main/java/devlight/io/sample/TopHorizontalNtbActivity.java
@@ -1,16 +1,18 @@
-package com.gigamole.navigationtabbar;
+package devlight.io.sample;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-import com.gigamole.library.NavigationTabBar;
+import devlight.io.library.ntb.NavigationTabBar;
import java.util.ArrayList;
import java.util.Random;
@@ -27,14 +29,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() {
@@ -56,10 +50,15 @@ public void destroyItem(final View container, final int position, final Object o
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
final View view = LayoutInflater.from(
- getBaseContext()).inflate(R.layout.vp_item, null, false);
+ getBaseContext()).inflate(R.layout.item_vp_list, null, false);
- final TextView txtPage = (TextView) view.findViewById(R.id.txt_vp_item_page);
- txtPage.setText(String.format("Page #%d", position));
+ final RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.rv);
+ recyclerView.setHasFixedSize(true);
+ recyclerView.setLayoutManager(new LinearLayoutManager(
+ getBaseContext(), LinearLayoutManager.VERTICAL, false
+ )
+ );
+ recyclerView.setAdapter(new RecycleAdapter());
container.addView(view);
return view;
@@ -70,25 +69,51 @@ public Object instantiateItem(final ViewGroup container, final int position) {
final NavigationTabBar navigationTabBar = (NavigationTabBar) findViewById(R.id.ntb_horizontal);
final ArrayList models = new ArrayList<>();
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_first), Color.parseColor(colors[0]), "Heart"));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_second), Color.parseColor(colors[1]), "Cup"));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_third), Color.parseColor(colors[2]), "Diploma"));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fourth), Color.parseColor(colors[3]), "Flag"));
- models.add(new NavigationTabBar.Model(
- getResources().getDrawable(R.drawable.ic_fifth), Color.parseColor(colors[4]), "Medal"));
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_first),
+ Color.parseColor(colors[0]))
+ .title("Heart")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_second),
+ Color.parseColor(colors[1]))
+ .title("Cup")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_third),
+ Color.parseColor(colors[2]))
+ .title("Diploma")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fourth),
+ Color.parseColor(colors[3]))
+ .title("Flag")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fifth),
+ Color.parseColor(colors[4]))
+ .title("Medal")
+ .build()
+ );
navigationTabBar.setModels(models);
navigationTabBar.setViewPager(viewPager, 2);
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();
+ viewPager.requestLayout();
}
});
@@ -123,4 +148,33 @@ public void run() {
}
});
}
+
+ public class RecycleAdapter extends RecyclerView.Adapter {
+
+ @Override
+ public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
+ final View view = LayoutInflater.from(getBaseContext()).inflate(R.layout.item_list, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, final int position) {
+ holder.txt.setText(String.format("Navigation Item #%d", position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return 20;
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+
+ public TextView txt;
+
+ public ViewHolder(final View itemView) {
+ super(itemView);
+ txt = (TextView) itemView.findViewById(R.id.txt_vp_item_list);
+ }
+ }
+ }
}
diff --git a/app/src/main/java/devlight/io/sample/VerticalNtbActivity.java b/app/src/main/java/devlight/io/sample/VerticalNtbActivity.java
new file mode 100644
index 0000000..29f3fb5
--- /dev/null
+++ b/app/src/main/java/devlight/io/sample/VerticalNtbActivity.java
@@ -0,0 +1,132 @@
+package devlight.io.sample;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import devlight.io.library.ntb.NavigationTabBar;
+
+import java.util.ArrayList;
+
+/**
+ * Created by GIGAMOLE on 28.03.2016.
+ */
+public class VerticalNtbActivity extends Activity {
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_vertical_ntb);
+ initUI();
+ }
+
+ private void initUI() {
+ final ViewPager viewPager = (ViewPager) findViewById(R.id.vp_vertical_ntb);
+ viewPager.setAdapter(new PagerAdapter() {
+ @Override
+ public int getCount() {
+ return 8;
+ }
+
+ @Override
+ public boolean isViewFromObject(final View view, final Object object) {
+ return view.equals(object);
+ }
+
+ @Override
+ public void destroyItem(final View container, final int position, final Object object) {
+ ((ViewPager) container).removeView((View) object);
+ }
+
+ @Override
+ public Object instantiateItem(final ViewGroup container, final int position) {
+ final View view = LayoutInflater.from(
+ getBaseContext()).inflate(R.layout.item_vp, null, false);
+
+ final TextView txtPage = (TextView) view.findViewById(R.id.txt_vp_item_page);
+ txtPage.setText(String.format("Page #%d", position));
+
+ container.addView(view);
+ return view;
+ }
+ });
+
+ final String[] colors = getResources().getStringArray(R.array.vertical_ntb);
+
+ final NavigationTabBar navigationTabBar = (NavigationTabBar) findViewById(R.id.ntb_vertical);
+ final ArrayList models = new ArrayList<>();
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_first),
+ Color.parseColor(colors[0]))
+ .title("ic_first")
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_second),
+ Color.parseColor(colors[1]))
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .title("ic_second")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_third),
+ Color.parseColor(colors[2]))
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .title("ic_third")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fourth),
+ Color.parseColor(colors[3]))
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .title("ic_fourth")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_fifth),
+ Color.parseColor(colors[4]))
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .title("ic_fifth")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_sixth),
+ Color.parseColor(colors[5]))
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .title("ic_sixth")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_seventh),
+ Color.parseColor(colors[6]))
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .title("ic_seventh")
+ .build()
+ );
+ models.add(
+ new NavigationTabBar.Model.Builder(
+ getResources().getDrawable(R.drawable.ic_eighth),
+ Color.parseColor(colors[7]))
+ .selectedIcon(getResources().getDrawable(R.drawable.ic_eighth))
+ .title("ic_eighth")
+ .build()
+ );
+
+ navigationTabBar.setModels(models);
+ navigationTabBar.setViewPager(viewPager, 4);
+ }
+}
diff --git a/app/src/main/res/drawable/bg_round_circle.xml b/app/src/main/res/drawable/bg_round_circle.xml
deleted file mode 100644
index 5e40942..0000000
--- a/app/src/main/res/drawable/bg_round_circle.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_horizontal_coordinator_ntb.xml b/app/src/main/res/layout/activity_horizontal_coordinator_ntb.xml
new file mode 100644
index 0000000..3655967
--- /dev/null
+++ b/app/src/main/res/layout/activity_horizontal_coordinator_ntb.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_horizontal_ntb.xml b/app/src/main/res/layout/activity_horizontal_ntb.xml
index 5832815..e3392b1 100644
--- a/app/src/main/res/layout/activity_horizontal_ntb.xml
+++ b/app/src/main/res/layout/activity_horizontal_ntb.xml
@@ -1,41 +1,30 @@
-
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
-
+ android:layout_height="60dp"
+ 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"
+ app:ntb_swiped="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 520622c..e9eb923 100644
--- a/app/src/main/res/layout/activity_horizontal_top_ntb.xml
+++ b/app/src/main/res/layout/activity_horizontal_top_ntb.xml
@@ -1,5 +1,5 @@
-
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
+
+
+
+
+
+
+
+
+
+
-
-
+ android:layout_gravity="bottom"/>
-
-
-
-
-
-
-
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 7b92cf2..42818b7 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:weightSum="4"
+ android:weightSum="5"
android:background="#423752">
-
+
-
@@ -39,12 +40,12 @@
android:layout_weight="1"
android:padding="10dp">
-
@@ -56,11 +57,12 @@
android:layout_weight="1"
android:padding="10dp">
-
@@ -82,11 +84,12 @@
android:layout_weight="1"
android:padding="10dp">
-
@@ -99,17 +102,18 @@
android:layout_weight="1"
android:padding="10dp">
-
+ app:ntb_inactive_color="#dddfec"
+ app:ntb_bg_color="#8d88e4"/>
@@ -119,12 +123,12 @@
android:layout_weight="1"
android:padding="10dp">
-
diff --git a/app/src/main/res/layout/activity_vertical_ntb.xml b/app/src/main/res/layout/activity_vertical_ntb.xml
index da6a74d..d6187b7 100644
--- a/app/src/main/res/layout/activity_vertical_ntb.xml
+++ b/app/src/main/res/layout/activity_vertical_ntb.xml
@@ -7,10 +7,12 @@
android:orientation="horizontal"
android:background="#423752">
-
diff --git a/app/src/main/res/layout/item_list.xml b/app/src/main/res/layout/item_list.xml
new file mode 100644
index 0000000..95fd221
--- /dev/null
+++ b/app/src/main/res/layout/item_list.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/vp_item.xml b/app/src/main/res/layout/item_vp.xml
similarity index 100%
rename from app/src/main/res/layout/vp_item.xml
rename to app/src/main/res/layout/item_vp.xml
diff --git a/app/src/main/res/layout/item_vp_list.xml b/app/src/main/res/layout/item_vp_list.xml
new file mode 100644
index 0000000..9474696
--- /dev/null
+++ b/app/src/main/res/layout/item_vp_list.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 52071e1..6c5d7ef 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -2,5 +2,5 @@
-
+
diff --git a/build.gradle b/build.gradle
index ba818b7..69bfa21 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,16 +3,18 @@
buildscript {
repositories {
jcenter()
+ google()
}
dependencies {
- classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.1"
- classpath 'com.android.tools.build:gradle:1.5.0'
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
+ classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3"
+ classpath 'com.android.tools.build:gradle:3.1.2'
}
}
allprojects {
repositories {
jcenter()
+ google()
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ec41c8e..9072f5b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Mar 28 17:22:59 EEST 2016
+#Fri Apr 27 03:13:54 BRT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
diff --git a/library/src/main/java/com/gigamole/library/NavigationTabBar.java b/library/src/main/java/com/gigamole/library/NavigationTabBar.java
deleted file mode 100644
index 8711fb8..0000000
--- a/library/src/main/java/com/gigamole/library/NavigationTabBar.java
+++ /dev/null
@@ -1,1431 +0,0 @@
-/*
- * Copyright (C) 2015 Basil Miller
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.gigamole.library;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.v4.view.ViewPager;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.widget.Scroller;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Random;
-
-/**
- * Created by GIGAMOLE on 24.03.2016.
- */
-public class NavigationTabBar extends View implements ViewPager.OnPageChangeListener {
-
- // NTP constants
- private final static String PREVIEW_BADGE = "0";
- private final static String PREVIEW_TITLE = "Title";
- private final static int INVALID_INDEX = -1;
-
- private final static int DEFAULT_BADGE_ANIMATION_DURATION = 200;
- private final static int DEFAULT_BADGE_REFRESH_ANIMATION_DURATION = 100;
- private final static int DEFAULT_ANIMATION_DURATION = 300;
- 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 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.35f;
- private final static float ICON_SIZE_FRACTION = 0.4f;
-
- private final static float TITLE_ACTIVE_ICON_SCALE_BY = 0.25f;
- private final static float TITLE_ICON_SIZE_FRACTION = 0.4f;
- 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 BADGE_HORIZONTAL_FRACTION = 0.5f;
- private final static float BADGE_VERTICAL_FRACTION = 0.75f;
- private final static float BADGE_TITLE_SIZE_FRACTION = 0.85f;
-
- private final static int ALL_INDEX = 0;
- private final static int ACTIVE_INDEX = 1;
-
- private final static int LEFT_INDEX = 0;
- private final static int CENTER_INDEX = 1;
- private final static int RIGHT_INDEX = 2;
-
- 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;
-
- private final static Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
- private final static Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
-
- // NTP and pointer bounds
- private RectF mBounds = new RectF();
- private RectF mPointerBounds = new RectF();
- // Badge bounds and bg badge bounds
- final Rect mBadgeBounds = new Rect();
- final RectF mBgBadgeBounds = new RectF();
-
- // Canvas, where all of other canvas will be merged
- private Bitmap mBitmap;
- private Canvas mCanvas;
-
- // Canvas with icons
- private Bitmap mIconsBitmap;
- private Canvas mIconsCanvas;
-
- // Canvas for our rect pointer
- private Bitmap mPointerBitmap;
- private Canvas mPointerCanvas;
-
- // Main paint
- private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG) {
- {
- setDither(true);
- setStyle(Style.FILL);
- }
- };
-
- // Pointer paint
- private Paint mPointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG) {
- {
- setDither(true);
- setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
- }
- };
-
- // Icons paint
- private Paint mIconPaint = new Paint(Paint.ANTI_ALIAS_FLAG) {
- {
- setDither(true);
- }
- };
-
- // Paint for icon mask pointer
- final Paint mIconPointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG) {
- {
- setStyle(Style.FILL);
- setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- }
- };
-
- // Paint for model title
- final Paint mModelTitlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG) {
- {
- setDither(true);
- setColor(Color.WHITE);
- setTextAlign(Align.CENTER);
- }
- };
-
- // Paint for badge
- final Paint mBadgePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG) {
- {
- setDither(true);
- setTextAlign(Align.CENTER);
- setFakeBoldText(true);
- }
- };
-
- // Variables for animator
- private ValueAnimator mAnimator = new ValueAnimator();
- private ResizeInterpolator mResizeInterpolator = new ResizeInterpolator();
- private int mAnimationDuration;
-
- // NTP models
- private ArrayList mModels = new ArrayList<>();
-
- // Variables for ViewPager
- private ViewPager mViewPager;
- private ViewPager.OnPageChangeListener mOnPageChangeListener;
- private int mScrollState;
-
- // Tab listener
- private OnTabBarSelectedIndexListener mOnTabBarSelectedIndexListener;
- private ValueAnimator.AnimatorListener mAnimatorListener;
-
- // Variables for sizes
- private float mModelSize;
- private int mIconSize;
- // Corners radius for rect mode
- private float mCornersRadius;
-
- // Model title size and margin
- private float mModelTitleSize;
- private float mTitleMargin;
-
- // Model badge title size and margin
- private float mBadgeMargin;
- private float mBadgeTitleSize;
-
- // Model title mode: active ar all
- private TitleMode mTitleMode;
- // Model badge position: left, center or right
- private BadgePosition mBadgePosition;
- // Model badge gravity: top or bottom
- private BadgeGravity mBadgeGravity;
-
- // Indexes
- private int mLastIndex = INVALID_INDEX;
- private int mIndex = INVALID_INDEX;
- // General fraction value
- private float mFraction;
-
- // Coordinates of pointer
- private float mStartPointerX;
- private float mEndPointerX;
- private float mPointerLeftTop;
- private float mPointerRightBottom;
-
- // Detect if model has title
- private boolean mIsTitled;
- // Detect if model has badge
- private boolean mIsBadged;
- // Detect if model badge have custom typeface
- private boolean mIsBadgeUseTypeface;
- // Detect if is bar mode or indicator pager mode
- private boolean mIsViewPagerMode;
- // Detect whether the horizontal orientation
- private boolean mIsHorizontalOrientation;
- // Detect if we move from left to right
- private boolean mIsResizeIn;
- // Detect if we get action down event
- private boolean mIsActionDown;
- // Detect if we get action down event on pointer
- private boolean mIsPointerActionDown;
- // Detect when we set index from tab bar nor from ViewPager
- private boolean mIsSetIndexFromTabBar;
-
- // Color variables
- private int mInactiveColor;
- private int mActiveColor;
-
- // Custom typeface
- private Typeface mTypeface;
-
- public NavigationTabBar(final Context context) {
- this(context, null);
- }
-
- public NavigationTabBar(final Context context, final AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public NavigationTabBar(final Context context, final AttributeSet attrs, final int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- //Init NTB
-
- // Always draw
- setWillNotDraw(false);
- // More speed!
- setLayerType(LAYER_TYPE_HARDWARE, null);
-
- final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NavigationTabBar);
- try {
- setIsTitled(
- typedArray.getBoolean(R.styleable.NavigationTabBar_ntb_titled, false)
- );
- setIsBadged(
- typedArray.getBoolean(R.styleable.NavigationTabBar_ntb_badged, false)
- );
- setIsBadgeUseTypeface(
- typedArray.getBoolean(R.styleable.NavigationTabBar_ntb_badge_use_typeface, false)
- );
- setTitleMode(
- typedArray.getInt(R.styleable.NavigationTabBar_ntb_title_mode, ALL_INDEX)
- );
- setBadgePosition(
- typedArray.getInt(R.styleable.NavigationTabBar_ntb_badge_position, RIGHT_INDEX)
- );
- setBadgeGravity(
- typedArray.getInt(R.styleable.NavigationTabBar_ntb_badge_gravity, TOP_INDEX)
- );
- setTypeface(
- typedArray.getString(R.styleable.NavigationTabBar_ntb_typeface)
- );
- setInactiveColor(
- typedArray.getColor(
- R.styleable.NavigationTabBar_ntb_inactive_color, DEFAULT_INACTIVE_COLOR
- )
- );
- setActiveColor(
- typedArray.getColor(
- R.styleable.NavigationTabBar_ntb_active_color, DEFAULT_ACTIVE_COLOR
- )
- );
- setAnimationDuration(
- typedArray.getInteger(
- R.styleable.NavigationTabBar_ntb_animation_duration, DEFAULT_ANIMATION_DURATION
- )
- );
- setCornersRadius(
- typedArray.getDimension(R.styleable.NavigationTabBar_ntb_corners_radius, 0.0f)
- );
-
- // Init animator
- mAnimator.setFloatValues(MIN_FRACTION, MAX_FRACTION);
- mAnimator.setInterpolator(new LinearInterpolator());
- mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(final ValueAnimator animation) {
- updateIndicatorPosition((Float) animation.getAnimatedValue());
- }
- });
-
- // Set preview models
- if (isInEditMode()) {
- // Get preview colors
- String[] previewColors = null;
- try {
- final int previewColorsId = typedArray.getResourceId(
- R.styleable.NavigationTabBar_ntb_preview_colors, 0
- );
- previewColors = previewColorsId == 0 ? null :
- typedArray.getResources().getStringArray(previewColorsId);
- } catch (Exception exception) {
- previewColors = null;
- exception.printStackTrace();
- } finally {
- if (previewColors == null)
- previewColors = typedArray.getResources().getStringArray(R.array.default_preview);
-
- for (String previewColor : previewColors)
- mModels.add(new Model(null, Color.parseColor(previewColor)));
- requestLayout();
- }
- }
- } finally {
- typedArray.recycle();
- }
- }
-
- public int getAnimationDuration() {
- return mAnimationDuration;
- }
-
- public void setAnimationDuration(final int animationDuration) {
- mAnimationDuration = animationDuration;
- mAnimator.setDuration(mAnimationDuration);
- resetScroller();
- }
-
- public ArrayList getModels() {
- return mModels;
- }
-
- public void setModels(final ArrayList models) {
- //Set update listeners to badge model animation
- for (final Model model : models) {
- model.mBadgeAnimator.removeAllUpdateListeners();
- model.mBadgeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(final ValueAnimator animation) {
- model.mBadgeFraction = (float) animation.getAnimatedValue();
- postInvalidate();
- }
- });
- }
-
- mModels.clear();
- mModels = models;
- requestLayout();
- }
-
- public boolean isTitled() {
- return mIsTitled;
- }
-
- public void setIsTitled(final boolean isTitled) {
- mIsTitled = isTitled;
- requestLayout();
- }
-
- public boolean isBadged() {
- return mIsBadged;
- }
-
- public void setIsBadged(final boolean isBadged) {
- mIsBadged = isBadged;
- requestLayout();
- }
-
- public boolean isBadgeUseTypeface() {
- return mIsBadgeUseTypeface;
- }
-
- public void setIsBadgeUseTypeface(final boolean isBadgeUseTypeface) {
- mIsBadgeUseTypeface = isBadgeUseTypeface;
- setBadgeTypeface();
- postInvalidate();
- }
-
- public TitleMode getTitleMode() {
- return mTitleMode;
- }
-
- private void setTitleMode(final int index) {
- switch (index) {
- case ACTIVE_INDEX:
- setTitleMode(TitleMode.ACTIVE);
- break;
- case ALL_INDEX:
- default:
- setTitleMode(TitleMode.ALL);
- }
- }
-
- public void setTitleMode(final TitleMode titleMode) {
- mTitleMode = titleMode;
- postInvalidate();
- }
-
- public BadgePosition getBadgePosition() {
- return mBadgePosition;
- }
-
- private void setBadgePosition(final int index) {
- switch (index) {
- case LEFT_INDEX:
- setBadgePosition(BadgePosition.LEFT);
- break;
- case CENTER_INDEX:
- setBadgePosition(BadgePosition.CENTER);
- break;
- case RIGHT_INDEX:
- default:
- setBadgePosition(BadgePosition.RIGHT);
- }
- }
-
- public void setBadgePosition(final BadgePosition badgePosition) {
- mBadgePosition = badgePosition;
- postInvalidate();
- }
-
- public BadgeGravity getBadgeGravity() {
- return mBadgeGravity;
- }
-
- private void setBadgeGravity(final int index) {
- switch (index) {
- case BOTTOM_INDEX:
- setBadgeGravity(BadgeGravity.BOTTOM);
- break;
- case TOP_INDEX:
- default:
- setBadgeGravity(BadgeGravity.TOP);
- }
- }
-
- public void setBadgeGravity(final BadgeGravity badgeGravity) {
- mBadgeGravity = badgeGravity;
- requestLayout();
- }
-
- public Typeface getTypeface() {
- return mTypeface;
- }
-
- public void setTypeface(final String typeface) {
- Typeface tempTypeface;
- try {
- tempTypeface = Typeface.createFromAsset(getContext().getAssets(), typeface);
- } catch (Exception e) {
- tempTypeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
- e.printStackTrace();
- }
-
- setTypeface(tempTypeface);
- }
-
- public void setTypeface(final Typeface typeface) {
- mTypeface = typeface;
- mModelTitlePaint.setTypeface(typeface);
- setBadgeTypeface();
- postInvalidate();
- }
-
- private void setBadgeTypeface() {
- mBadgePaint.setTypeface(
- mIsBadgeUseTypeface ? mTypeface : Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
- );
- }
-
- public int getActiveColor() {
- return mActiveColor;
- }
-
- public void setActiveColor(final int activeColor) {
- mActiveColor = activeColor;
- mIconPointerPaint.setColor(activeColor);
- postInvalidate();
- }
-
- public int getInactiveColor() {
- return mInactiveColor;
- }
-
- public void setInactiveColor(final int inactiveColor) {
- mInactiveColor = inactiveColor;
-
- // Set color filter to wrap icons with inactive color
- mIconPaint.setColorFilter(new PorterDuffColorFilter(inactiveColor, PorterDuff.Mode.SRC_IN));
- mModelTitlePaint.setColor(mInactiveColor);
- postInvalidate();
- }
-
- public float getCornersRadius() {
- return mCornersRadius;
- }
-
- public void setCornersRadius(final float cornersRadius) {
- mCornersRadius = cornersRadius;
- postInvalidate();
- }
-
- public float getBadgeMargin() {
- return mBadgeMargin;
- }
-
- public float getBarHeight() {
- return mBounds.height();
- }
-
- public OnTabBarSelectedIndexListener getOnTabBarSelectedIndexListener() {
- return mOnTabBarSelectedIndexListener;
- }
-
- // Set on tab bar selected index listener where you can trigger action onStart or onEnd
- public void setOnTabBarSelectedIndexListener(final OnTabBarSelectedIndexListener onTabBarSelectedIndexListener) {
- mOnTabBarSelectedIndexListener = onTabBarSelectedIndexListener;
-
- if (mAnimatorListener == null)
- mAnimatorListener = new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(final Animator animation) {
- if (mOnTabBarSelectedIndexListener != null && !mIsViewPagerMode)
- mOnTabBarSelectedIndexListener.onStartTabSelected(mModels.get(mIndex), mIndex);
- }
-
- @Override
- public void onAnimationEnd(final Animator animation) {
- if (mOnTabBarSelectedIndexListener != null && !mIsViewPagerMode)
- mOnTabBarSelectedIndexListener.onEndTabSelected(mModels.get(mIndex), mIndex);
- }
-
- @Override
- public void onAnimationCancel(final Animator animation) {
-
- }
-
- @Override
- public void onAnimationRepeat(final Animator animation) {
-
- }
- };
- mAnimator.removeListener(mAnimatorListener);
- mAnimator.addListener(mAnimatorListener);
- }
-
- public void setViewPager(final ViewPager viewPager) {
- // Detect whether ViewPager mode
- if (viewPager == null) {
- mIsViewPagerMode = false;
- return;
- }
-
- if (mViewPager == viewPager) return;
- if (mViewPager != null) mViewPager.setOnPageChangeListener(null);
- if (viewPager.getAdapter() == null)
- throw new IllegalStateException("ViewPager does not provide adapter instance.");
-
- mIsViewPagerMode = true;
- mViewPager = viewPager;
- mViewPager.addOnPageChangeListener(this);
-
- resetScroller();
- postInvalidate();
- }
-
- public void setViewPager(final ViewPager viewPager, int index) {
- setViewPager(viewPager);
-
- mIndex = index;
- if (mIsViewPagerMode) mViewPager.setCurrentItem(index, true);
- postInvalidate();
- }
-
- // Reset scroller and reset scroll duration equals to animation duration
- private void resetScroller() {
- if (mViewPager == null) return;
- try {
- final Field scrollerField = ViewPager.class.getDeclaredField("mScroller");
- scrollerField.setAccessible(true);
- final ResizeViewPagerScroller scroller = new ResizeViewPagerScroller(getContext());
- scrollerField.set(mViewPager, scroller);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public void setOnPageChangeListener(final ViewPager.OnPageChangeListener listener) {
- mOnPageChangeListener = listener;
- }
-
- public int getModelIndex() {
- return mIndex;
- }
-
- public void setModelIndex(int index) {
- setModelIndex(index, false);
- }
-
- // Set model index from touch or programmatically
- public void setModelIndex(int index, boolean force) {
- if (mAnimator.isRunning()) return;
- if (mModels.isEmpty()) return;
-
- // This check gives us opportunity to have an non selected model
- if (mIndex == INVALID_INDEX) force = true;
-
- // Detect if last is the same
- if (index == mIndex) return;
-
- // Snap index to models size
- index = Math.max(0, Math.min(index, mModels.size() - 1));
-
- mIsResizeIn = index < mIndex;
- mLastIndex = mIndex;
- mIndex = index;
-
- mIsSetIndexFromTabBar = true;
- if (mIsViewPagerMode) {
- if (mViewPager == null) throw new IllegalStateException("ViewPager is null.");
- mViewPager.setCurrentItem(index, true);
- }
-
- // Set startX and endX for animation, where we animate two sides of rect with different interpolation
- mStartPointerX = mPointerLeftTop;
- mEndPointerX = mIndex * mModelSize;
-
- // If it force, so update immediately, else animate
- // This happens if we set index onCreate or something like this
- // You can use force param or call this method in some post()
- if (force) updateIndicatorPosition(MAX_FRACTION);
- else mAnimator.start();
- }
-
- private void updateIndicatorPosition(final float fraction) {
- // Update general fraction
- mFraction = fraction;
-
- // Set the pointer left top side coordinate
- mPointerLeftTop =
- mStartPointerX + (mResizeInterpolator.getResizeInterpolation(fraction, mIsResizeIn) *
- (mEndPointerX - mStartPointerX));
- // Set the pointer right bottom side coordinate
- mPointerRightBottom =
- (mStartPointerX + mModelSize) +
- (mResizeInterpolator.getResizeInterpolation(fraction, !mIsResizeIn) *
- (mEndPointerX - mStartPointerX));
-
- // Update pointer
- postInvalidate();
- }
-
- // Update NTP
- private void notifyDataSetChanged() {
- postInvalidate();
- }
-
- @Override
- public boolean onTouchEvent(final MotionEvent event) {
- // Return if animation is running
- if (mAnimator.isRunning()) return true;
- // If is not idle state, return
- if (mScrollState != ViewPager.SCROLL_STATE_IDLE) return true;
-
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // Action down touch
- mIsActionDown = true;
- if (!mIsViewPagerMode) break;
- // Detect if we touch down on pointer, later to move
- if (mIsHorizontalOrientation)
- mIsPointerActionDown = (int) (event.getX() / mModelSize) == mIndex;
- else
- mIsPointerActionDown = (int) (event.getY() / mModelSize) == mIndex;
- break;
- case MotionEvent.ACTION_MOVE:
- // If pointer touched, so move
- if (mIsPointerActionDown) {
- if (mIsHorizontalOrientation)
- mViewPager.setCurrentItem((int) (event.getX() / mModelSize), true);
- else
- mViewPager.setCurrentItem((int) (event.getY() / mModelSize), true);
- break;
- }
- if (mIsActionDown) break;
- case MotionEvent.ACTION_UP:
- // Press up and set model index relative to current coordinate
- if (mIsActionDown) {
- if (mIsHorizontalOrientation) setModelIndex((int) (event.getX() / mModelSize));
- else setModelIndex((int) (event.getY() / mModelSize));
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_OUTSIDE:
- default:
- // Reset action touch variables
- mIsPointerActionDown = false;
- mIsActionDown = false;
- break;
- }
-
- return true;
- }
-
- @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);
-
- if (mModels.isEmpty() || width == 0 || height == 0) return;
-
- // Detect orientation and calculate icon size
- if (width > height) {
- mIsHorizontalOrientation = true;
-
- // Get smaller side
- float side = mModelSize > height ? height : mModelSize;
- if (mIsBadged) side -= side * TITLE_SIZE_FRACTION;
-
- mModelSize = (float) width / (float) mModels.size();
- mIconSize = (int) (side * (mIsTitled ? TITLE_ICON_SIZE_FRACTION : ICON_SIZE_FRACTION));
-
- mModelTitleSize = side * TITLE_SIZE_FRACTION;
- mTitleMargin = side * TITLE_MARGIN_FRACTION;
-
- // If is badged mode, so get vars and set paint with default bounds
- if (mIsBadged) {
- mBadgeTitleSize = mModelTitleSize * BADGE_TITLE_SIZE_FRACTION;
-
- final Rect badgeBounds = new Rect();
- mBadgePaint.setTextSize(mBadgeTitleSize);
- mBadgePaint.getTextBounds(PREVIEW_BADGE, 0, 1, badgeBounds);
- mBadgeMargin = (badgeBounds.height() * 0.5f) +
- (mBadgeTitleSize * BADGE_HORIZONTAL_FRACTION * BADGE_VERTICAL_FRACTION);
- }
- } else {
- mIsHorizontalOrientation = false;
- mIsTitled = false;
- mIsBadged = false;
-
- mModelSize = (float) height / (float) mModels.size();
- mIconSize = (int) ((mModelSize > width ? width : mModelSize) * ICON_SIZE_FRACTION);
- }
-
- // Set bounds for NTB
- mBounds.set(0.0f, 0.0f, width, height - mBadgeMargin);
-
- // Set main bitmap
- mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mCanvas = new Canvas(mBitmap);
-
- // Set pointer canvas
- mPointerBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mPointerCanvas = new Canvas(mPointerBitmap);
-
- // Set icons canvas
- mIconsBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mIconsCanvas = new Canvas(mIconsBitmap);
-
- // Set scale fraction for icons
- for (Model model : mModels) {
- final float originalIconSize = model.mIcon.getWidth() > model.mIcon.getHeight() ?
- model.mIcon.getWidth() : model.mIcon.getHeight();
- model.mInactiveIconScale = (float) mIconSize / originalIconSize;
- model.mActiveIconScaleBy = model.mInactiveIconScale *
- (mIsTitled ? TITLE_ACTIVE_ICON_SCALE_BY : ACTIVE_ICON_SCALE_BY);
- }
-
- // Set start position of pointer for preview or on start
- if (isInEditMode() || !mIsViewPagerMode) {
- mIsSetIndexFromTabBar = true;
-
- // Set random in preview mode
- if (isInEditMode()) {
- mIndex = new Random().nextInt(mModels.size());
-
- if (mIsBadged)
- for (int i = 0; i < mModels.size(); i++) {
- final Model model = mModels.get(i);
-
- if (i == mIndex) {
- model.mBadgeFraction = MAX_FRACTION;
- model.showBadge();
- } else {
- model.mBadgeFraction = MIN_FRACTION;
- model.hideBadge();
- }
- }
- }
-
- mStartPointerX = mIndex * mModelSize;
- mEndPointerX = mStartPointerX;
- updateIndicatorPosition(MAX_FRACTION);
- }
- }
-
- @Override
- protected void onDraw(final Canvas canvas) {
- if (mCanvas == null || mPointerCanvas == null || mIconsCanvas == null) return;
-
- // Reset and clear canvases
- mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
- mPointerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
- mIconsCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
-
- // Get pointer badge margin for gravity
- final float pointerBadgeMargin = mBadgeGravity == BadgeGravity.TOP ? mBadgeMargin : 0.0f;
-
- // Draw our model colors
- for (int i = 0; i < mModels.size(); i++) {
- mPaint.setColor(mModels.get(i).getColor());
-
- if (mIsHorizontalOrientation) {
- final float left = mModelSize * i;
- final float right = left + mModelSize;
- mCanvas.drawRect(
- left, pointerBadgeMargin, right, mBounds.height() + pointerBadgeMargin, mPaint
- );
- } else {
- final float top = mModelSize * i;
- final float bottom = top + mModelSize;
- mCanvas.drawRect(0.0f, top, mBounds.width(), bottom, mPaint);
- }
- }
-
- // Set bound of pointer
- if (mIsHorizontalOrientation)
- mPointerBounds.set(
- mPointerLeftTop, pointerBadgeMargin,
- mPointerRightBottom, mBounds.height() + pointerBadgeMargin
- );
- 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);
-
- // Draw model icons
- for (int i = 0; i < mModels.size(); i++) {
- final Model model = mModels.get(i);
-
- // Variables to center our icons
- final float leftOffset;
- final float topOffset;
- final float matrixCenterX;
- final float matrixCenterY;
-
- // Set vars for icon when model with title or without
- final float iconMarginTitleHeight = mIconSize + mTitleMargin + mModelTitleSize;
- final float leftTitleOffset = (mModelSize * i) + (mModelSize * 0.5f);
- final float topTitleOffset =
- 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;
-
- matrixCenterX = leftOffset + model.mIcon.getWidth() * 0.5f;
- matrixCenterY = topOffset + model.mIcon.getHeight() * 0.5f +
- (mIsTitled && mTitleMode == TitleMode.ALL ? mTitleMargin * 0.5f : 0.0f);
- } else {
- leftOffset = (mBounds.width() - model.mIcon.getWidth()) * 0.5f;
- topOffset = (mModelSize * i) + (mModelSize - model.mIcon.getHeight()) * 0.5f;
-
- matrixCenterX = leftOffset + model.mIcon.getWidth() * 0.5f;
- matrixCenterY = topOffset + model.mIcon.getHeight() * 0.5f;
- }
-
- // Title translate position
- final float titleTranslate = -model.mIcon.getHeight() + topTitleOffset - mTitleMargin * 0.5f;
-
- // Translate icon to model center
- model.mIconMatrix.setTranslate(
- leftOffset,
- (mIsTitled && mTitleMode == TitleMode.ALL) ? titleTranslate : topOffset
- );
-
- // Get interpolated fraction for left last and current models
- final float interpolation = mResizeInterpolator.getResizeInterpolation(mFraction, true);
- final float lastInterpolation = mResizeInterpolator.getResizeInterpolation(mFraction, false);
-
- // Scale value relative to interpolation
- final float matrixScale = model.mActiveIconScaleBy * interpolation;
- final float matrixLastScale = model.mActiveIconScaleBy * lastInterpolation;
-
- // Get title alpha relative to interpolation
- final int titleAlpha = (int) (MAX_ALPHA * interpolation);
- final int titleLastAlpha = MAX_ALPHA - (int) (MAX_ALPHA * lastInterpolation);
- // Get title scale relative to interpolation
- final float titleScale = MAX_FRACTION + (interpolation * TITLE_ACTIVE_SCALE_BY);
- final float titleLastScale = (MAX_FRACTION + TITLE_ACTIVE_SCALE_BY) -
- (lastInterpolation * TITLE_ACTIVE_SCALE_BY);
-
- // Check if we handle models from touch on NTP or from ViewPager
- // There is a strange logic of ViewPager onPageScrolled method, so it is
- if (mIsSetIndexFromTabBar) {
- if (mIndex == i)
- updateCurrentModel(
- model, leftOffset, topOffset, titleTranslate, interpolation,
- matrixCenterX, matrixCenterY, matrixScale, titleScale, titleAlpha
- );
- else if (mLastIndex == i)
- updateLastModel(
- model, leftOffset, topOffset, titleTranslate, lastInterpolation,
- matrixCenterX, matrixCenterY, matrixLastScale, titleLastScale, titleLastAlpha
- );
- else
- updateInactiveModel(
- model, leftOffset, topOffset, matrixCenterX, matrixCenterY
- );
- } else {
- if (i != mIndex && i != mIndex + 1)
- updateInactiveModel(
- model, leftOffset, topOffset, matrixCenterX, matrixCenterY
- );
- else if (i == mIndex + 1)
- updateCurrentModel(
- model, leftOffset, topOffset, titleTranslate, interpolation,
- matrixCenterX, matrixCenterY, matrixScale, titleScale, titleAlpha
- );
- else if (i == mIndex)
- updateLastModel(
- model, leftOffset, topOffset, titleTranslate, lastInterpolation,
- matrixCenterX, matrixCenterY, matrixLastScale, titleLastScale, titleLastAlpha
- );
- }
-
- // Draw model icon
- mIconsCanvas.drawBitmap(model.mIcon, model.mIconMatrix, mIconPaint);
- if (mIsTitled)
- mIconsCanvas.drawText(
- isInEditMode() ? PREVIEW_TITLE : model.getTitle(),
- leftTitleOffset, topTitleOffset, mModelTitlePaint
- );
- }
-
- // Draw pointer with active color to wrap out active icon
- if (mCornersRadius == 0) mIconsCanvas.drawRect(mPointerBounds, mIconPointerPaint);
- else mIconsCanvas.drawRoundRect(mPointerBounds, mCornersRadius, mCornersRadius, mIconPointerPaint);
-
- // Draw general bitmap
- canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null);
- // Draw icons bitmap on top
- canvas.drawBitmap( mIconsBitmap, 0.0f, pointerBadgeMargin, null);
-
- // If is not badged, exit
- if (!mIsBadged) return;
-
- // Model badge margin and offset relative to gravity mode
- final float modelBadgeMargin =
- mBadgeGravity == BadgeGravity.TOP ? mBadgeMargin : mBounds.height();
- final float modelBadgeOffset =
- mBadgeGravity == BadgeGravity.TOP ? 0.0f : mBounds.height() - mBadgeMargin;
-
- for (int i = 0; i < mModels.size(); i++) {
- final Model model = mModels.get(i);
-
- // Set preview badge title
- if (isInEditMode() || TextUtils.isEmpty(model.getBadgeTitle()))
- model.setBadgeTitle(PREVIEW_BADGE);
-
- // Set badge title bounds
- mBadgePaint.setTextSize(mBadgeTitleSize * model.mBadgeFraction);
- mBadgePaint.getTextBounds(
- model.getBadgeTitle(), 0, model.getBadgeTitle().length(), mBadgeBounds
- );
-
- // Get horizontal and vertical padding for bg
- final float horizontalPadding = mBadgeTitleSize * BADGE_HORIZONTAL_FRACTION;
- final float verticalPadding = horizontalPadding * BADGE_VERTICAL_FRACTION;
-
- // Set horizontal badge offset
- final float badgeBoundsHorizontalOffset =
- (mModelSize * i) + (mModelSize * mBadgePosition.mPositionFraction);
-
- // If is badge title only one char, so create circle else round rect
- 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()
- );
-
- // Set color and alpha for badge bg
- if (model.mBadgeFraction == MIN_FRACTION) mBadgePaint.setColor(Color.TRANSPARENT);
- else mBadgePaint.setColor(mActiveColor);
- mBadgePaint.setAlpha((int) (MAX_ALPHA * model.mBadgeFraction));
-
- // Set corners to round rect for badge bg and draw
- 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(model.getColor());
- 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 -
- mBadgeBounds.bottom + modelBadgeOffset;
- canvas.drawText(
- model.getBadgeTitle(), badgeBoundsHorizontalOffset, badgeVerticalOffset +
- mBadgeBounds.height() - (mBadgeBounds.height() * model.mBadgeFraction),
- mBadgePaint
- );
- }
- }
-
- // Method to transform current fraction of NTB and position
- private void updateCurrentModel(
- final Model model,
- final float leftOffset,
- final float topOffset,
- final float titleTranslate,
- final float interpolation,
- final float matrixCenterX,
- final float matrixCenterY,
- final float matrixScale,
- final float textScale,
- final int textAlpha
- ) {
- if (mIsTitled && mTitleMode == TitleMode.ACTIVE)
- model.mIconMatrix.setTranslate(
- leftOffset, topOffset - (interpolation * (topOffset - titleTranslate))
- );
-
- model.mIconMatrix.postScale(
- model.mInactiveIconScale + matrixScale, model.mInactiveIconScale + matrixScale,
- matrixCenterX, matrixCenterY + (mIsTitled && mTitleMode == TitleMode.ACTIVE ?
- mTitleMargin * 0.5f * interpolation : 0.0f)
- );
-
- mModelTitlePaint.setTextSize(mModelTitleSize * textScale);
- if (mTitleMode == TitleMode.ACTIVE) mModelTitlePaint.setAlpha(textAlpha);
- }
-
- // Method to transform last fraction of NTB and position
- private void updateLastModel(
- final Model model,
- final float leftOffset,
- final float topOffset,
- final float titleTranslate,
- final float lastInterpolation,
- final float matrixCenterX,
- final float matrixCenterY,
- final float matrixLastScale,
- final float textLastScale,
- final int textLastAlpha
- ) {
- if (mIsTitled && mTitleMode == TitleMode.ACTIVE)
- model.mIconMatrix.setTranslate(
- leftOffset, titleTranslate + (lastInterpolation * (topOffset - titleTranslate))
- );
-
- model.mIconMatrix.postScale(
- model.mInactiveIconScale + model.mActiveIconScaleBy - matrixLastScale,
- model.mInactiveIconScale + model.mActiveIconScaleBy - matrixLastScale,
- matrixCenterX, matrixCenterY + (mIsTitled && mTitleMode == TitleMode.ACTIVE ?
- mTitleMargin * 0.5f - (mTitleMargin * 0.5f * lastInterpolation) : 0.0f)
- );
-
- mModelTitlePaint.setTextSize(mModelTitleSize * textLastScale);
- if (mTitleMode == TitleMode.ACTIVE) mModelTitlePaint.setAlpha(textLastAlpha);
- }
-
- // Method to transform others fraction of NTB and position
- private void updateInactiveModel(
- final Model model,
- final float leftOffset,
- final float topOffset,
- final float matrixCenterX,
- final float matrixCenterY
- ) {
- if (mIsTitled && mTitleMode == TitleMode.ACTIVE)
- model.mIconMatrix.setTranslate(leftOffset, topOffset);
-
- model.mIconMatrix.postScale(
- model.mInactiveIconScale, model.mInactiveIconScale, matrixCenterX, matrixCenterY
- );
-
- mModelTitlePaint.setTextSize(mModelTitleSize);
- if (mTitleMode == TitleMode.ACTIVE) mModelTitlePaint.setAlpha(MIN_ALPHA);
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset, final int positionOffsetPixels) {
- // If we animate, don`t call this
- if (!mIsSetIndexFromTabBar) {
- mIsResizeIn = position < mIndex;
- mLastIndex = mIndex;
- mIndex = position;
-
- mStartPointerX = position * mModelSize;
- mEndPointerX = mStartPointerX + mModelSize;
- updateIndicatorPosition(positionOffset);
- }
-
- if (mOnPageChangeListener != null)
- mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
- }
-
- @Override
- public void onPageSelected(final int position) {
- // If VP idle, so update
- if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
- mIsResizeIn = position < mIndex;
- mLastIndex = mIndex;
- mIndex = position;
- postInvalidate();
- }
- }
-
- @Override
- public void onPageScrollStateChanged(final int state) {
- // If VP idle, reset to MIN_FRACTION
- if (state == ViewPager.SCROLL_STATE_IDLE) {
- mFraction = MIN_FRACTION;
- mIsSetIndexFromTabBar = false;
-
- if (mOnPageChangeListener != null) mOnPageChangeListener.onPageSelected(mIndex);
- else {
- if (mOnTabBarSelectedIndexListener != null)
- mOnTabBarSelectedIndexListener.onEndTabSelected(mModels.get(mIndex), mIndex);
- }
- }
- mScrollState = state;
-
- if (mOnPageChangeListener != null) mOnPageChangeListener.onPageScrollStateChanged(state);
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- final SavedState savedState = (SavedState) state;
- super.onRestoreInstanceState(savedState.getSuperState());
- mIndex = savedState.index;
- requestLayout();
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- final Parcelable superState = super.onSaveInstanceState();
- final SavedState savedState = new SavedState(superState);
- savedState.index = mIndex;
- return savedState;
- }
-
- static class SavedState extends BaseSavedState {
- int index;
-
- public SavedState(Parcelable superState) {
- super(superState);
- }
-
- private SavedState(Parcel in) {
- super(in);
- index = in.readInt();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(index);
- }
-
- @SuppressWarnings("UnusedDeclaration")
- public static final Creator CREATOR = new Creator() {
- @Override
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- // Model class
- public static class Model {
-
- private String mTitle = "";
- private int mColor;
-
- private Bitmap mIcon;
- private Matrix mIconMatrix = new Matrix();
-
- private String mBadgeTitle = "";
- private String mTempBadgeTitle = "";
- private float mBadgeFraction;
-
- private boolean mIsBadgeShowed;
- private boolean mIsBadgeUpdated;
-
- private ValueAnimator mBadgeAnimator = new ValueAnimator();
-
- private float mInactiveIconScale;
- private float mActiveIconScaleBy;
-
- public Model(final Drawable icon, final int color) {
- mColor = color;
- mIcon = icon != null ? ((BitmapDrawable) icon).getBitmap() :
- Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565);
-
- mBadgeAnimator.addListener(new Animator.AnimatorListener() {
-
- @Override
- public void onAnimationStart(final Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(final Animator animation) {
- // Detect whether we just update text and don`t reset show state
- if (!mIsBadgeUpdated) mIsBadgeShowed = !mIsBadgeShowed;
- else mIsBadgeUpdated = false;
- }
-
- @Override
- public void onAnimationCancel(final Animator animation) {
-
- }
-
- @Override
- public void onAnimationRepeat(final Animator animation) {
- // Change title when we update and don`t see the title
- if (mIsBadgeUpdated) mBadgeTitle = mTempBadgeTitle;
- }
- });
- }
-
- public Model(final Drawable icon, final int color, final String title) {
- this(icon, color);
- mTitle = title;
- }
-
- public Model(final Drawable icon, final int color, final String title, final String badgeTitle) {
- this(icon, color, title);
- mBadgeTitle = badgeTitle;
- }
-
- public String getTitle() {
- return mTitle;
- }
-
- public void setTitle(final String title) {
- mTitle = title;
- }
-
- public int getColor() {
- return mColor;
- }
-
- public void setColor(final int color) {
- mColor = color;
- }
-
- public boolean isBadgeShowed() {
- return mIsBadgeShowed;
- }
-
- public String getBadgeTitle() {
- return mBadgeTitle;
- }
-
- public void setBadgeTitle(final String badgeTitle) {
- mBadgeTitle = badgeTitle;
- }
-
- // If your badge is visible on screen, so you can update title with animation
- public void updateBadgeTitle(final String badgeTitle) {
- if (!mIsBadgeShowed) return;
- if (mBadgeAnimator.isRunning()) mBadgeAnimator.end();
-
- mTempBadgeTitle = badgeTitle;
- mIsBadgeUpdated = true;
-
- mBadgeAnimator.setFloatValues(MAX_FRACTION, MIN_FRACTION);
- mBadgeAnimator.setDuration(DEFAULT_BADGE_REFRESH_ANIMATION_DURATION);
- mBadgeAnimator.setRepeatMode(ValueAnimator.REVERSE);
- mBadgeAnimator.setRepeatCount(1);
- mBadgeAnimator.start();
- }
-
- public void toggleBadge() {
- if (mBadgeAnimator.isRunning()) mBadgeAnimator.end();
- if (mIsBadgeShowed) hideBadge();
- else showBadge();
- }
-
- public void showBadge() {
- mIsBadgeUpdated = false;
-
- if (mBadgeAnimator.isRunning()) mBadgeAnimator.end();
- if (mIsBadgeShowed) return;
-
- mBadgeAnimator.setFloatValues(MIN_FRACTION, MAX_FRACTION);
- mBadgeAnimator.setInterpolator(DECELERATE_INTERPOLATOR);
- mBadgeAnimator.setDuration(DEFAULT_BADGE_ANIMATION_DURATION);
- mBadgeAnimator.setRepeatMode(ValueAnimator.RESTART);
- mBadgeAnimator.setRepeatCount(0);
- mBadgeAnimator.start();
- }
-
- public void hideBadge() {
- mIsBadgeUpdated = false;
-
- if (mBadgeAnimator.isRunning()) mBadgeAnimator.end();
- if (!mIsBadgeShowed) return;
-
- mBadgeAnimator.setFloatValues(MAX_FRACTION, MIN_FRACTION);
- mBadgeAnimator.setInterpolator(ACCELERATE_INTERPOLATOR);
- mBadgeAnimator.setDuration(DEFAULT_BADGE_ANIMATION_DURATION);
- mBadgeAnimator.setRepeatMode(ValueAnimator.RESTART);
- mBadgeAnimator.setRepeatCount(0);
- mBadgeAnimator.start();
- }
- }
-
- // Custom scroller with custom scroll duration
- private class ResizeViewPagerScroller extends Scroller {
-
- public ResizeViewPagerScroller(Context context) {
- super(context, new AccelerateDecelerateInterpolator());
- }
-
- @Override
- public void startScroll(int startX, int startY, int dx, int dy, int duration) {
- super.startScroll(startX, startY, dx, dy, mAnimationDuration);
- }
-
- @Override
- public void startScroll(int startX, int startY, int dx, int dy) {
- super.startScroll(startX, startY, dx, dy, mAnimationDuration);
- }
- }
-
- // Resize interpolator to create smooth effect on pointer according to inspiration design
- // This is like improved accelerated and decelerated interpolator
- private class ResizeInterpolator implements Interpolator {
-
- // Spring factor
- private final float mFactor = 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));
- }
-
- public float getResizeInterpolation(final float input, final boolean resizeIn) {
- mResizeIn = resizeIn;
- return getInterpolation(input);
- }
- }
-
- // Model title mode
- public enum TitleMode {
- ALL, ACTIVE
- }
-
- // Model badge position
- public enum BadgePosition {
-
- LEFT(LEFT_FRACTION), CENTER(CENTER_FRACTION), RIGHT(RIGHT_FRACTION);
-
- private float mPositionFraction;
-
- BadgePosition() {
- mPositionFraction = RIGHT_FRACTION;
- }
-
- BadgePosition(final float positionFraction) {
- mPositionFraction = positionFraction;
- }
- }
-
- // Model badge gravity
- public enum BadgeGravity {
- TOP, BOTTOM
- }
-
- // Out listener for selected index
- public interface OnTabBarSelectedIndexListener {
- void onStartTabSelected(final Model model, final int index);
-
- void onEndTabSelected(final Model model, final int index);
- }
-}
diff --git a/library/.gitignore b/navigationtabbar/.gitignore
similarity index 100%
rename from library/.gitignore
rename to navigationtabbar/.gitignore
diff --git a/library/build.gradle b/navigationtabbar/build.gradle
similarity index 84%
rename from library/build.gradle
rename to navigationtabbar/build.gradle
index 7ad9b86..04d0b47 100644
--- a/library/build.gradle
+++ b/navigationtabbar/build.gradle
@@ -19,17 +19,17 @@ apply plugin: "com.jfrog.bintray"
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'maven'
-version = "1.1.1"
+version = "1.2.5"
android {
- compileSdkVersion 23
- buildToolsVersion "23.0.2"
+ compileSdkVersion 27
+ buildToolsVersion '27.0.3'
defaultConfig {
- minSdkVersion 11
- targetSdkVersion 23
+ minSdkVersion 14
+ targetSdkVersion 27
versionCode 1
- versionName "1.1.1"
+ versionName "1.2.5"
}
buildTypes {
release {
@@ -40,23 +40,25 @@ android {
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.android.support:appcompat-v7:23.1.0'
- compile 'org.jsoup:jsoup:+'
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ //noinspection GradleDependency
+ implementation 'com.android.support:design:27.1.1'
}
def siteUrl = 'https://github.com/DevLight-Mobile-Agency/NavigationTabBar'
def gitUrl = 'https://github.com/DevLight-Mobile-Agency/NavigationTabBar.git'
-group = "com.github.devlight.navigationtabbar"
+group = "devlight.io"
install {
repositories.mavenInstaller {
// This generates POM.xml with proper parameters
pom {
+ //noinspection GroovyAssignabilityCheck
project {
packaging 'aar'
// Add your description here
+ //noinspection GroovyAssignabilityCheck
name 'NavigationTabBar'
description = 'Navigation tab bar with colorful interactions.'
url siteUrl
@@ -64,6 +66,7 @@ install {
// Set your license
licenses {
license {
+ //noinspection GroovyAssignabilityCheck
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
@@ -71,6 +74,7 @@ install {
developers {
developer {
id 'gigamole'
+ //noinspection GroovyAssignabilityCheck
name 'Basil Miller'
email 'gigamole53@gmail.com'
}
@@ -86,17 +90,20 @@ install {
}
task sourcesJar(type: Jar) {
+ //noinspection GroovyAssignabilityCheck
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
+ //noinspection GroovyAssignabilityCheck
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
+ //noinspection GrUnresolvedAccess
from javadoc.destinationDir
}
artifacts {
@@ -115,11 +122,12 @@ bintray {
pkg {
repo = "maven"
// it is the name that appears in bintray when logged
- name = "navigationtabbar"
+ name = "ntb"
websiteUrl = siteUrl
vcsUrl = gitUrl
licenses = ["Apache-2.0"]
publish = true
+ //noinspection GroovyAssignabilityCheck
version {
gpg {
sign = true //Determines whether to GPG sign the files. The default is false
diff --git a/library/proguard-rules.pro b/navigationtabbar/proguard-rules.pro
similarity index 100%
rename from library/proguard-rules.pro
rename to navigationtabbar/proguard-rules.pro
diff --git a/app/src/androidTest/java/com/gigamole/navigationtabbar/ApplicationTest.java b/navigationtabbar/src/androidTest/java/devlight/io/navigationtabbar/ApplicationTest.java
similarity index 89%
rename from app/src/androidTest/java/com/gigamole/navigationtabbar/ApplicationTest.java
rename to navigationtabbar/src/androidTest/java/devlight/io/navigationtabbar/ApplicationTest.java
index c41d296..4db3138 100644
--- a/app/src/androidTest/java/com/gigamole/navigationtabbar/ApplicationTest.java
+++ b/navigationtabbar/src/androidTest/java/devlight/io/navigationtabbar/ApplicationTest.java
@@ -1,4 +1,4 @@
-package com.gigamole.navigationtabbar;
+package devlight.io.navigationtabbar;
import android.app.Application;
import android.test.ApplicationTestCase;
diff --git a/library/src/main/AndroidManifest.xml b/navigationtabbar/src/main/AndroidManifest.xml
similarity index 87%
rename from library/src/main/AndroidManifest.xml
rename to navigationtabbar/src/main/AndroidManifest.xml
index b562cfd..bae526b 100644
--- a/library/src/main/AndroidManifest.xml
+++ b/navigationtabbar/src/main/AndroidManifest.xml
@@ -11,15 +11,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-
-
-
+ android:supportsRtl="true"/>
diff --git a/navigationtabbar/src/main/java/devlight/io/library/behavior/NavigationTabBarBehavior.java b/navigationtabbar/src/main/java/devlight/io/library/behavior/NavigationTabBarBehavior.java
new file mode 100644
index 0000000..58b66e9
--- /dev/null
+++ b/navigationtabbar/src/main/java/devlight/io/library/behavior/NavigationTabBarBehavior.java
@@ -0,0 +1,291 @@
+package devlight.io.library.behavior;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+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.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+
+import devlight.io.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;
+ }
+
+ @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(dependency);
+ return super.layoutDependsOn(parent, child, dependency);
+ }
+
+ @Override
+ public void onNestedVerticalOverScroll() {
+ // This method is intentionally empty, because of override
+ }
+
+ @Override
+ public void onDirectionNestedPreScroll() {
+// This method is intentionally empty, because of override
+ }
+
+ @Override
+ protected boolean onNestedDirectionFling() {
+ 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 snack bar
+ 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();
+ }
+ }
+ });
+ }
+
+ @SuppressWarnings("unused")
+ 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
+ @SuppressWarnings("SameParameterValue")
+ public void resetOffset(NavigationTabBar view, boolean withAnimation) {
+ if (mHidden) {
+ mHidden = false;
+ animateOffset(view, 0, true, withAnimation);
+ }
+ }
+
+ // Update snack bar bottom margin
+ private 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());
+
+ child.bringToFront();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ dependency.setStateListAnimator(null);
+ dependency.setElevation(0.0F);
+ }
+
+ 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
+ private void updateFloatingActionButton(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/navigationtabbar/src/main/java/devlight/io/library/behavior/VerticalScrollingBehavior.java b/navigationtabbar/src/main/java/devlight/io/library/behavior/VerticalScrollingBehavior.java
new file mode 100644
index 0000000..e27b134
--- /dev/null
+++ b/navigationtabbar/src/main/java/devlight/io/library/behavior/VerticalScrollingBehavior.java
@@ -0,0 +1,127 @@
+package devlight.io.library.behavior;
+
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.Parcelable;
+import android.support.annotation.IntDef;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.v4.view.WindowInsetsCompat;
+import android.view.View;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@SuppressWarnings({"unused", "EmptyMethod"})
+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})
+ @interface ScrollDirection {
+ int SCROLL_DIRECTION_UP = 1;
+ int SCROLL_DIRECTION_DOWN = -1;
+ int SCROLL_NONE = 0;
+ }
+
+ VerticalScrollingBehavior() {
+ super();
+ }
+
+ @ScrollDirection
+ public int getOverScrollDirection() {
+ return mOverScrollDirection;
+ }
+
+ @ScrollDirection
+ public int getScrollDirection() {
+ return mScrollDirection;
+ }
+
+ // Direction of the over scroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN
+ // Unconsumed value, negative or positive based on the direction
+ // Cumulative value for current direction
+ protected abstract void onNestedVerticalOverScroll();
+
+ // Direction of the over scroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN
+ protected abstract void onDirectionNestedPreScroll();
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ @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();
+ }
+
+ @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();
+ }
+
+ @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();
+ }
+
+ @SuppressWarnings("SameReturnValue")
+ protected abstract boolean onNestedDirectionFling();
+
+ @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/navigationtabbar/src/main/java/devlight/io/library/ntb/NavigationTabBar.java b/navigationtabbar/src/main/java/devlight/io/library/ntb/NavigationTabBar.java
new file mode 100644
index 0000000..a6f83fa
--- /dev/null
+++ b/navigationtabbar/src/main/java/devlight/io/library/ntb/NavigationTabBar.java
@@ -0,0 +1,1981 @@
+/*
+ * Copyright (C) 2015 Basil Miller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package devlight.io.library.ntb;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.animation.LinearOutSlowInInterpolator;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.Scroller;
+
+import com.gigamole.navigationtabbar.R;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import devlight.io.library.behavior.NavigationTabBarBehavior;
+
+/**
+ * Created by GIGAMOLE on 24.03.2016.
+ */
+@SuppressWarnings({"unused", "DefaultFileTemplate"})
+public class NavigationTabBar extends View implements ViewPager.OnPageChangeListener {
+
+ // NTB constants
+ protected final static int FLAGS =
+ Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG;
+
+ protected final static String PREVIEW_BADGE = "0";
+ protected final static String PREVIEW_TITLE = "Title";
+
+ protected final static int INVALID_INDEX = -1;
+ public final static int AUTO_SIZE = -2;
+ public final static int AUTO_COLOR = -3;
+ public final static int AUTO_SCALE = -4;
+
+ protected final static int DEFAULT_BADGE_ANIMATION_DURATION = 200;
+ protected final static int DEFAULT_BADGE_REFRESH_ANIMATION_DURATION = 100;
+ protected final static int DEFAULT_ANIMATION_DURATION = 300;
+ protected final static float DEFAULT_ICON_SIZE_FRACTION = 0.5F;
+ protected final static float DEFAULT_TITLE_ICON_SIZE_FRACTION = 0.5F;
+
+ protected final static int DEFAULT_INACTIVE_COLOR = Color.parseColor("#9f90af");
+ protected final static int DEFAULT_ACTIVE_COLOR = Color.WHITE;
+ protected final static int DEFAULT_BG_COLOR = Color.parseColor("#605271");
+
+ protected final static float MIN_FRACTION = 0.0F;
+ protected final static float MAX_FRACTION = 1.0F;
+
+ protected final static int MIN_ALPHA = 0;
+ protected final static int MAX_ALPHA = 255;
+
+ protected final static float SCALED_FRACTION = 0.3F;
+ protected final static float TITLE_ACTIVE_ICON_SCALE_BY = 0.2F;
+ protected final static float TITLE_ACTIVE_SCALE_BY = 0.2F;
+ protected final static float TITLE_SIZE_FRACTION = 0.2F;
+ protected final static float TITLE_MARGIN_FRACTION = 0.15F;
+ protected final static float TITLE_MARGIN_SCALE_FRACTION = 0.25F;
+
+ protected final static float BADGE_HORIZONTAL_FRACTION = 0.5F;
+ protected final static float BADGE_VERTICAL_FRACTION = 0.75F;
+ protected final static float BADGE_TITLE_SIZE_FRACTION = 0.9F;
+
+ protected final static float LEFT_FRACTION = 0.25F;
+ protected final static float CENTER_FRACTION = 0.5F;
+ protected final static float RIGHT_FRACTION = 0.75F;
+
+ protected final static Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
+ protected final static Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
+ protected final static Interpolator OUT_SLOW_IN_INTERPOLATOR = new LinearOutSlowInInterpolator();
+
+ // NTB and pointer bounds
+ protected final RectF mBounds = new RectF();
+ protected final RectF mBgBounds = new RectF();
+ protected final RectF mPointerBounds = new RectF();
+ // Badge bounds and bg badge bounds
+ protected final Rect mBadgeBounds = new Rect();
+ protected final RectF mBgBadgeBounds = new RectF();
+
+ // Canvas, where all of other canvas will be merged
+ protected Bitmap mBitmap;
+ protected final Canvas mCanvas = new Canvas();
+
+ // Canvas with icons
+ protected Bitmap mIconsBitmap;
+ protected final Canvas mIconsCanvas = new Canvas();
+
+ // Canvas with titles
+ protected Bitmap mTitlesBitmap;
+ protected final Canvas mTitlesCanvas = new Canvas();
+
+ // Canvas for our rect pointer
+ protected Bitmap mPointerBitmap;
+ protected final Canvas mPointerCanvas = new Canvas();
+
+ // External background view for the NTB
+ protected NavigationTabBarBehavior mBehavior;
+
+ // Detect if behavior already set
+ protected boolean mIsBehaviorSet;
+ // Detect if behavior enabled
+ protected boolean mBehaviorEnabled;
+ // Detect if need to hide NTB
+ protected boolean mNeedHide;
+ // Detect if need animate animate or force hide
+ protected boolean mAnimateHide;
+
+ // Main paint
+ protected final Paint mPaint = new Paint(FLAGS) {
+ {
+ setStyle(Style.FILL);
+ }
+ };
+ // Background color paint
+ protected final Paint mBgPaint = new Paint(FLAGS) {
+ {
+ setStyle(Style.FILL);
+ }
+ };
+ // Pointer paint
+ protected final Paint mPointerPaint = new Paint(FLAGS) {
+ {
+ setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
+ }
+ };
+
+ // Icons paint
+ protected final Paint mIconPaint = new Paint(FLAGS);
+ protected final Paint mSelectedIconPaint = new Paint(FLAGS);
+
+ // Paint for icon mask pointer
+ protected final Paint mIconPointerPaint = new Paint(FLAGS) {
+ {
+ setStyle(Style.FILL);
+ setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ }
+ };
+
+ // Paint for model title
+ protected final Paint mModelTitlePaint = new TextPaint(FLAGS) {
+ {
+ setColor(Color.WHITE);
+ setTextAlign(Align.CENTER);
+ }
+ };
+
+ // Paint for badge
+ protected final Paint mBadgePaint = new TextPaint(FLAGS) {
+ {
+ setTextAlign(Align.CENTER);
+ setFakeBoldText(true);
+ }
+ };
+
+ // Variables for animator
+ protected final ValueAnimator mAnimator = new ValueAnimator();
+ protected final ResizeInterpolator mResizeInterpolator = new ResizeInterpolator();
+ protected int mAnimationDuration;
+
+ // NTB models
+ protected final List mModels = new ArrayList<>();
+
+ // Variables for ViewPager
+ protected ViewPager mViewPager;
+ protected ViewPager.OnPageChangeListener mOnPageChangeListener;
+ protected int mScrollState;
+
+ // Tab listener
+ protected OnTabBarSelectedIndexListener mOnTabBarSelectedIndexListener;
+ protected ValueAnimator.AnimatorListener mAnimatorListener;
+
+ // Variables for sizes
+ protected float mModelSize;
+ protected float mIconSize;
+ protected float mIconSizeFraction;
+ // Corners radius for rect mode
+ protected float mCornersRadius;
+
+ // Model title size and margin
+ protected float mModelTitleSize = AUTO_SIZE;
+ protected float mTitleMargin;
+
+ // Model badge title size and margin
+ protected float mBadgeMargin;
+ protected float mBadgeTitleSize = AUTO_SIZE;
+
+ // Model title mode: active ar all
+ protected TitleMode mTitleMode;
+ // Model badge position: left, center or right
+ protected BadgePosition mBadgePosition;
+ // Model badge gravity: top or bottom
+ protected BadgeGravity mBadgeGravity;
+
+ // Model badge bg and title color.
+ // By default badge bg color is the active model color and badge title color is the model bg color
+ // To reset colors just set bg and title color to AUTO_COLOR
+ protected int mBadgeTitleColor = AUTO_COLOR;
+ protected int mBadgeBgColor = AUTO_COLOR;
+
+ // Indexes
+ protected int mLastIndex = INVALID_INDEX;
+ protected int mIndex = INVALID_INDEX;
+ // General fraction value
+ protected float mFraction;
+
+ // Coordinates of pointer
+ protected float mStartPointerX;
+ protected float mEndPointerX;
+ protected float mPointerLeftTop;
+ protected float mPointerRightBottom;
+
+ // Detect if model has title
+ protected boolean mIsTitled;
+ // Detect if model has badge
+ protected boolean mIsBadged;
+ // Detect if model icon scaled
+ protected boolean mIsScaled;
+ // Detect if model icon tinted
+ protected boolean mIsTinted;
+ // Detect if model can swiped
+ protected boolean mIsSwiped;
+ // Detect if model badge have custom typeface
+ protected boolean mIsBadgeUseTypeface;
+ // Detect if is bar mode or indicator pager mode
+ protected boolean mIsViewPagerMode;
+ // Detect whether the horizontal orientation
+ protected boolean mIsHorizontalOrientation;
+ // Detect if we move from left to right
+ protected boolean mIsResizeIn;
+ // Detect if we get action down event
+ protected boolean mIsActionDown;
+ // Detect if we get action down event on pointer
+ protected boolean mIsPointerActionDown;
+ // Detect when we set index from tab bar nor from ViewPager
+ protected boolean mIsSetIndexFromTabBar;
+
+ // Color variables
+ protected int mInactiveColor;
+ protected int mActiveColor;
+ protected int mBgColor;
+
+ // Custom typeface
+ protected Typeface mTypeface;
+
+ public NavigationTabBar(final Context context) {
+ this(context, null);
+ }
+
+ public NavigationTabBar(final Context context, final AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ @SuppressWarnings({"ResourceAsColor", "ResourceType"})
+ public NavigationTabBar(final Context context, final AttributeSet attrs, final int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ //Init NTB
+
+ // Always draw
+ setWillNotDraw(false);
+ // Speed and fix for pre 17 API
+ ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, null);
+ setLayerType(LAYER_TYPE_SOFTWARE, null);
+
+ final TypedArray typedArray =
+ context.obtainStyledAttributes(attrs, R.styleable.NavigationTabBar);
+ try {
+ setIsTitled(typedArray.getBoolean(R.styleable.NavigationTabBar_ntb_titled, false));
+ setIsBadged(typedArray.getBoolean(R.styleable.NavigationTabBar_ntb_badged, false));
+ setIsScaled(typedArray.getBoolean(R.styleable.NavigationTabBar_ntb_scaled, true));
+ setIsTinted(typedArray.getBoolean(R.styleable.NavigationTabBar_ntb_tinted, true));
+ setIsSwiped(typedArray.getBoolean(R.styleable.NavigationTabBar_ntb_swiped, true));
+ setTitleSize(
+ typedArray.getDimension(R.styleable.NavigationTabBar_ntb_title_size, AUTO_SIZE)
+ );
+ setIsBadgeUseTypeface(
+ typedArray.getBoolean(
+ R.styleable.NavigationTabBar_ntb_badge_use_typeface,
+ false
+ )
+ );
+
+ setTitleMode(
+ typedArray.getInt(
+ R.styleable.NavigationTabBar_ntb_title_mode, TitleMode.ALL_INDEX
+ )
+ );
+ setBadgeSize(
+ typedArray.getDimension(R.styleable.NavigationTabBar_ntb_badge_size, AUTO_SIZE)
+ );
+ setBadgePosition(
+ typedArray.getInt(
+ R.styleable.NavigationTabBar_ntb_badge_position,
+ BadgePosition.RIGHT_INDEX
+ )
+ );
+ setBadgeGravity(
+ typedArray.getInt(
+ R.styleable.NavigationTabBar_ntb_badge_gravity, BadgeGravity.TOP_INDEX
+ )
+ );
+ setBadgeBgColor(
+ typedArray.getColor(R.styleable.NavigationTabBar_ntb_badge_bg_color, AUTO_COLOR)
+ );
+ setBadgeTitleColor(
+ typedArray.getColor(
+ R.styleable.NavigationTabBar_ntb_badge_title_color, AUTO_COLOR
+ )
+ );
+
+ setTypeface(typedArray.getString(R.styleable.NavigationTabBar_ntb_typeface));
+ setInactiveColor(
+ typedArray.getColor(
+ R.styleable.NavigationTabBar_ntb_inactive_color, DEFAULT_INACTIVE_COLOR
+ )
+ );
+ setActiveColor(
+ typedArray.getColor(
+ R.styleable.NavigationTabBar_ntb_active_color, DEFAULT_ACTIVE_COLOR
+ )
+ );
+ setBgColor(
+ typedArray.getColor(
+ R.styleable.NavigationTabBar_ntb_bg_color, DEFAULT_BG_COLOR
+ )
+ );
+ setAnimationDuration(
+ typedArray.getInteger(
+ R.styleable.NavigationTabBar_ntb_animation_duration,
+ DEFAULT_ANIMATION_DURATION
+ )
+ );
+ setCornersRadius(
+ typedArray.getDimension(R.styleable.NavigationTabBar_ntb_corners_radius, 0.0F)
+ );
+ setIconSizeFraction(
+ typedArray.getFloat(
+ R.styleable.NavigationTabBar_ntb_icon_size_fraction,
+ AUTO_SCALE
+ )
+ );
+
+ // Init animator
+ mAnimator.setFloatValues(MIN_FRACTION, MAX_FRACTION);
+ mAnimator.setInterpolator(new LinearInterpolator());
+ mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(final ValueAnimator animation) {
+ updateIndicatorPosition((Float) animation.getAnimatedValue());
+ }
+ });
+
+ // Set preview models
+ if (isInEditMode()) {
+ // Get preview colors
+ String[] previewColors = null;
+ try {
+ final int previewColorsId = typedArray.getResourceId(
+ R.styleable.NavigationTabBar_ntb_preview_colors, 0
+ );
+ previewColors = previewColorsId == 0 ? null :
+ typedArray.getResources().getStringArray(previewColorsId);
+ } catch (Exception exception) {
+ previewColors = null;
+ exception.printStackTrace();
+ } finally {
+ if (previewColors == null)
+ previewColors =
+ typedArray.getResources().getStringArray(R.array.default_preview);
+
+ for (String previewColor : previewColors)
+ mModels.add(new Model.Builder(null, Color.parseColor(previewColor)).build());
+ requestLayout();
+ }
+ }
+ } finally {
+ typedArray.recycle();
+ }
+ }
+
+ public int getAnimationDuration() {
+ return mAnimationDuration;
+ }
+
+ public void setAnimationDuration(final int animationDuration) {
+ mAnimationDuration = animationDuration;
+ mAnimator.setDuration(mAnimationDuration);
+ resetScroller();
+ }
+
+ public List getModels() {
+ return mModels;
+ }
+
+ public void setModels(final List models) {
+ //Set update listeners to badge model animation
+ for (final Model model : models) {
+ model.mBadgeAnimator.removeAllUpdateListeners();
+ model.mBadgeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(final ValueAnimator animation) {
+ model.mBadgeFraction = (float) animation.getAnimatedValue();
+ postInvalidate();
+ }
+ });
+ }
+
+ mModels.clear();
+ mModels.addAll(models);
+ requestLayout();
+ }
+
+ public boolean isTitled() {
+ return mIsTitled;
+ }
+
+ public void setIsTitled(final boolean isTitled) {
+ mIsTitled = isTitled;
+ requestLayout();
+ }
+
+ public boolean isBadged() {
+ return mIsBadged;
+ }
+
+ public void setIsBadged(final boolean isBadged) {
+ mIsBadged = isBadged;
+ requestLayout();
+ }
+
+ public boolean isScaled() {
+ return mIsScaled;
+ }
+
+ public void setIsScaled(final boolean isScaled) {
+ mIsScaled = isScaled;
+ requestLayout();
+ }
+
+ public boolean isTinted() {
+ return mIsTinted;
+ }
+
+ public void setIsTinted(final boolean isTinted) {
+ mIsTinted = isTinted;
+ updateTint();
+ }
+
+ public boolean isSwiped() {
+ return mIsSwiped;
+ }
+
+ public void setIsSwiped(final boolean swiped) {
+ mIsSwiped = swiped;
+ }
+
+ public float getTitleSize() {
+ return mModelTitleSize;
+ }
+
+ // To reset title size to automatic just put in method AUTO_SIZE value
+ public void setTitleSize(final float modelTitleSize) {
+ mModelTitleSize = modelTitleSize;
+ if (modelTitleSize == AUTO_SIZE) requestLayout();
+ }
+
+ public boolean isBadgeUseTypeface() {
+ return mIsBadgeUseTypeface;
+ }
+
+ public void setIsBadgeUseTypeface(final boolean isBadgeUseTypeface) {
+ mIsBadgeUseTypeface = isBadgeUseTypeface;
+ setBadgeTypeface();
+ postInvalidate();
+ }
+
+ public TitleMode getTitleMode() {
+ return mTitleMode;
+ }
+
+ protected void setTitleMode(final int index) {
+ switch (index) {
+ case TitleMode.ACTIVE_INDEX:
+ setTitleMode(TitleMode.ACTIVE);
+ break;
+ case TitleMode.ALL_INDEX:
+ default:
+ setTitleMode(TitleMode.ALL);
+ break;
+ }
+ }
+
+ public void setTitleMode(final TitleMode titleMode) {
+ mTitleMode = titleMode;
+ postInvalidate();
+ }
+
+ public BadgePosition getBadgePosition() {
+ return mBadgePosition;
+ }
+
+ protected void setBadgePosition(final int index) {
+ switch (index) {
+ case BadgePosition.LEFT_INDEX:
+ setBadgePosition(BadgePosition.LEFT);
+ break;
+ case BadgePosition.CENTER_INDEX:
+ setBadgePosition(BadgePosition.CENTER);
+ break;
+ case BadgePosition.RIGHT_INDEX:
+ default:
+ setBadgePosition(BadgePosition.RIGHT);
+ break;
+ }
+ }
+
+ public void setBadgePosition(final BadgePosition badgePosition) {
+ mBadgePosition = badgePosition;
+ postInvalidate();
+ }
+
+ public BadgeGravity getBadgeGravity() {
+ return mBadgeGravity;
+ }
+
+ protected void setBadgeGravity(final int index) {
+ switch (index) {
+ case BadgeGravity.BOTTOM_INDEX:
+ setBadgeGravity(BadgeGravity.BOTTOM);
+ break;
+ case BadgeGravity.TOP_INDEX:
+ default:
+ setBadgeGravity(BadgeGravity.TOP);
+ break;
+ }
+ }
+
+ public void setBadgeGravity(final BadgeGravity badgeGravity) {
+ mBadgeGravity = badgeGravity;
+ requestLayout();
+ }
+
+ public int getBadgeBgColor() {
+ return mBadgeBgColor;
+ }
+
+ public void setBadgeBgColor(final int badgeBgColor) {
+ mBadgeBgColor = badgeBgColor;
+ }
+
+ public int getBadgeTitleColor() {
+ return mBadgeTitleColor;
+ }
+
+ public void setBadgeTitleColor(final int badgeTitleColor) {
+ mBadgeTitleColor = badgeTitleColor;
+ }
+
+ public float getBadgeSize() {
+ return mBadgeTitleSize;
+ }
+
+ // To reset badge title size to automatic just put in method AUTO_SIZE value
+ public void setBadgeSize(final float badgeTitleSize) {
+ mBadgeTitleSize = badgeTitleSize;
+ if (mBadgeTitleSize == AUTO_SIZE) requestLayout();
+ }
+
+ public Typeface getTypeface() {
+ return mTypeface;
+ }
+
+ public void setTypeface(final String typeface) {
+ if (TextUtils.isEmpty(typeface)) return;
+
+ Typeface tempTypeface;
+ try {
+ tempTypeface = Typeface.createFromAsset(getContext().getAssets(), typeface);
+ } catch (Exception e) {
+ tempTypeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
+ e.printStackTrace();
+ }
+
+ setTypeface(tempTypeface);
+ }
+
+ public void setTypeface(final Typeface typeface) {
+ mTypeface = typeface;
+ mModelTitlePaint.setTypeface(typeface);
+ setBadgeTypeface();
+ postInvalidate();
+ }
+
+ protected void setBadgeTypeface() {
+ mBadgePaint.setTypeface(
+ mIsBadgeUseTypeface ? mTypeface : Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
+ );
+ }
+
+ public int getActiveColor() {
+ return mActiveColor;
+ }
+
+ public void setActiveColor(final int activeColor) {
+ mActiveColor = activeColor;
+
+ // Set icon pointer active color
+ mIconPointerPaint.setColor(mActiveColor);
+ updateTint();
+ }
+
+ public int getInactiveColor() {
+ return mInactiveColor;
+ }
+
+ public void setInactiveColor(final int inactiveColor) {
+ mInactiveColor = inactiveColor;
+
+ // Set inactive color to title
+ mModelTitlePaint.setColor(mInactiveColor);
+ updateTint();
+ }
+
+ public int getBgColor() {
+ return mBgColor;
+ }
+
+ public void setBgColor(final int bgColor) {
+ mBgColor = bgColor;
+ mBgPaint.setColor(mBgColor);
+ postInvalidate();
+ }
+
+ public float getCornersRadius() {
+ return mCornersRadius;
+ }
+
+ public void setCornersRadius(final float cornersRadius) {
+ mCornersRadius = cornersRadius;
+ postInvalidate();
+ }
+
+ public float getIconSizeFraction() {
+ return mIconSizeFraction;
+ }
+
+ // To reset scale fraction of icon to automatic just put in method AUTO_SCALE value
+ public void setIconSizeFraction(final float iconSizeFraction) {
+ mIconSizeFraction = iconSizeFraction;
+ requestLayout();
+ }
+
+ public float getBadgeMargin() {
+ return mBadgeMargin;
+ }
+
+ public float getBarHeight() {
+ return mBounds.height();
+ }
+
+ public OnTabBarSelectedIndexListener getOnTabBarSelectedIndexListener() {
+ return mOnTabBarSelectedIndexListener;
+ }
+
+ // Set on tab bar selected index listener where you can trigger action onStart or onEnd
+ public void setOnTabBarSelectedIndexListener(
+ final OnTabBarSelectedIndexListener onTabBarSelectedIndexListener
+ ) {
+ mOnTabBarSelectedIndexListener = onTabBarSelectedIndexListener;
+
+ if (mAnimatorListener == null)
+ mAnimatorListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(final Animator animation) {
+ if (mOnTabBarSelectedIndexListener != null)
+ mOnTabBarSelectedIndexListener.onStartTabSelected(
+ mModels.get(mIndex), mIndex
+ );
+
+ animation.removeListener(this);
+ animation.addListener(this);
+ }
+
+ @Override
+ public void onAnimationEnd(final Animator animation) {
+ if (mIsViewPagerMode) return;
+
+ animation.removeListener(this);
+ animation.addListener(this);
+
+ if (mOnTabBarSelectedIndexListener != null)
+ mOnTabBarSelectedIndexListener.onEndTabSelected(
+ mModels.get(mIndex), mIndex
+ );
+ }
+ };
+ mAnimator.removeListener(mAnimatorListener);
+ mAnimator.addListener(mAnimatorListener);
+ }
+
+ public void setViewPager(final ViewPager viewPager) {
+ // Detect whether ViewPager mode
+ if (viewPager == null) {
+ mIsViewPagerMode = false;
+ return;
+ }
+
+ if (viewPager.equals(mViewPager)) return;
+ if (mViewPager != null) //noinspection deprecation
+ mViewPager.setOnPageChangeListener(null);
+ if (viewPager.getAdapter() == null)
+ throw new IllegalStateException("ViewPager does not provide adapter instance.");
+
+ mIsViewPagerMode = true;
+ mViewPager = viewPager;
+ mViewPager.removeOnPageChangeListener(this);
+ mViewPager.addOnPageChangeListener(this);
+
+ resetScroller();
+ postInvalidate();
+ }
+
+ public void setViewPager(final ViewPager viewPager, int index) {
+ setViewPager(viewPager);
+
+ mIndex = index;
+ if (mIsViewPagerMode) mViewPager.setCurrentItem(index, true);
+ postInvalidate();
+ }
+
+ // Reset scroller and reset scroll duration equals to animation duration
+ protected void resetScroller() {
+ if (mViewPager == null) return;
+ try {
+ final Field scrollerField = ViewPager.class.getDeclaredField("mScroller");
+ scrollerField.setAccessible(true);
+ final ResizeViewPagerScroller scroller = new ResizeViewPagerScroller(getContext());
+ scrollerField.set(mViewPager, scroller);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void setOnPageChangeListener(final ViewPager.OnPageChangeListener listener) {
+ 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;
+ }
+
+ public void setModelIndex(int index) {
+ setModelIndex(index, false);
+ }
+
+ // Set model index from touch or programmatically
+ public void setModelIndex(final int modelIndex, final boolean isForce) {
+ if (mAnimator.isRunning()) return;
+ if (mModels.isEmpty()) return;
+
+ int index = modelIndex;
+ boolean force = isForce;
+
+ // This check gives us opportunity to have an non selected model
+ if (mIndex == INVALID_INDEX) force = true;
+ // Detect if last is the same
+ if (index == mIndex) force = true;
+ // Snap index to models size
+ index = Math.max(0, Math.min(index, mModels.size() - 1));
+
+ mIsResizeIn = index < mIndex;
+ mLastIndex = mIndex;
+ mIndex = index;
+
+ mIsSetIndexFromTabBar = true;
+ if (mIsViewPagerMode) {
+ if (mViewPager == null) throw new IllegalStateException("ViewPager is null.");
+ mViewPager.setCurrentItem(index, !force);
+ }
+
+ // Set startX and endX for animation,
+ // where we animate two sides of rect with different interpolation
+ if (force) {
+ mStartPointerX = mIndex * mModelSize;
+ mEndPointerX = mStartPointerX;
+ } else {
+ mStartPointerX = mPointerLeftTop;
+ mEndPointerX = mIndex * mModelSize;
+ }
+
+ // If it force, so update immediately, else animate
+ // This happens if we set index onCreate or something like this
+ // You can use force param or call this method in some post()
+ if (force) {
+ updateIndicatorPosition(MAX_FRACTION);
+
+ if (mOnTabBarSelectedIndexListener != null)
+ mOnTabBarSelectedIndexListener.onStartTabSelected(mModels.get(mIndex), mIndex);
+
+ // Force onPageScrolled listener and refresh VP
+ if (mIsViewPagerMode) {
+ if (!mViewPager.isFakeDragging()) mViewPager.beginFakeDrag();
+ if (mViewPager.isFakeDragging()) mViewPager.fakeDragBy(0.0F);
+ if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
+ } else {
+ if (mOnTabBarSelectedIndexListener != null)
+ mOnTabBarSelectedIndexListener.onEndTabSelected(mModels.get(mIndex), mIndex);
+ }
+ } else mAnimator.start();
+ }
+
+ // Deselect active index and reset pointer
+ public void deselect() {
+ mLastIndex = INVALID_INDEX;
+ mIndex = INVALID_INDEX;
+ mStartPointerX = INVALID_INDEX * mModelSize;
+ mEndPointerX = mStartPointerX;
+ updateIndicatorPosition(MIN_FRACTION);
+ }
+
+ protected void updateIndicatorPosition(final float fraction) {
+ // Update general fraction
+ mFraction = fraction;
+
+ // Set the pointer left top side coordinate
+ mPointerLeftTop = mStartPointerX +
+ (mResizeInterpolator.getResizeInterpolation(fraction, mIsResizeIn) *
+ (mEndPointerX - mStartPointerX));
+ // Set the pointer right bottom side coordinate
+ mPointerRightBottom = (mStartPointerX + mModelSize) +
+ (mResizeInterpolator.getResizeInterpolation(fraction, !mIsResizeIn) *
+ (mEndPointerX - mStartPointerX));
+
+ // Update pointer
+ postInvalidate();
+ }
+
+ // Update NTB
+ protected void notifyDataSetChanged() {
+ requestLayout();
+ postInvalidate();
+ }
+
+ @Override
+ public boolean onTouchEvent(final MotionEvent event) {
+ // Return if animation is running
+ if (mAnimator.isRunning()) return true;
+ // If is not idle state, return
+ if (mScrollState != ViewPager.SCROLL_STATE_IDLE) return true;
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ // Action down touch
+ mIsActionDown = true;
+ if (!mIsViewPagerMode) break;
+ if (!mIsSwiped) break;
+ // Detect if we touch down on pointer, later to move
+ if (mIsHorizontalOrientation)
+ mIsPointerActionDown = (int) (event.getX() / mModelSize) == mIndex;
+ else
+ mIsPointerActionDown = (int) (event.getY() / mModelSize) == mIndex;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // If pointer touched, so move
+ if (mIsPointerActionDown) {
+ if (mIsHorizontalOrientation)
+ mViewPager.setCurrentItem((int) (event.getX() / mModelSize), true);
+ else
+ mViewPager.setCurrentItem((int) (event.getY() / mModelSize), true);
+ break;
+ }
+ if (mIsActionDown) break;
+ case MotionEvent.ACTION_UP:
+ // Press up and set model index relative to current coordinate
+ if (mIsActionDown) {
+ playSoundEffect(SoundEffectConstants.CLICK);
+ if (mIsHorizontalOrientation) setModelIndex((int) (event.getX() / mModelSize));
+ else setModelIndex((int) (event.getY() / mModelSize));
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_OUTSIDE:
+ default:
+ // Reset action touch variables
+ mIsPointerActionDown = false;
+ mIsActionDown = false;
+ break;
+ }
+
+ 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);
+
+ if (mModels.isEmpty() || width == 0 || height == 0) return;
+
+ // Detect orientation and calculate icon size
+ if (width > height) {
+ mIsHorizontalOrientation = true;
+
+ // Get model size
+ mModelSize = (float) width / (float) mModels.size();
+
+ // Get smaller side
+ float side = mModelSize > height ? height : mModelSize;
+ if (mIsBadged) side -= side * TITLE_SIZE_FRACTION;
+
+ mIconSize = side * (mIconSizeFraction != AUTO_SCALE ? mIconSizeFraction :
+ (mIsTitled ? DEFAULT_TITLE_ICON_SIZE_FRACTION : DEFAULT_ICON_SIZE_FRACTION));
+ if (mModelTitleSize == AUTO_SIZE) mModelTitleSize = side * TITLE_SIZE_FRACTION;
+ mTitleMargin = side * TITLE_MARGIN_FRACTION;
+
+ // If is badged mode, so get vars and set paint with default bounds
+ if (mIsBadged) {
+ if (mBadgeTitleSize == AUTO_SIZE)
+ mBadgeTitleSize = (side * TITLE_SIZE_FRACTION) * BADGE_TITLE_SIZE_FRACTION;
+
+ final Rect badgeBounds = new Rect();
+ mBadgePaint.setTextSize(mBadgeTitleSize);
+ mBadgePaint.getTextBounds(PREVIEW_BADGE, 0, 1, badgeBounds);
+ 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;
+// mIsTitled = false;
+ mIsBadged = false;
+
+ mModelSize = (float) height / (float) mModels.size();
+ // Get smaller side
+ float side = mModelSize > width ? width : mModelSize;
+
+ mIconSize = (int) (side *
+ (mIconSizeFraction == AUTO_SCALE ?
+ DEFAULT_ICON_SIZE_FRACTION : mIconSizeFraction));
+
+ if (mModelTitleSize == AUTO_SIZE) mModelTitleSize = side * TITLE_SIZE_FRACTION;
+ mTitleMargin = side * TITLE_MARGIN_FRACTION;
+ }
+
+ // Set bounds for NTB
+ mBounds.set(0.0F, 0.0F, width, height - mBadgeMargin);
+
+ final float barBadgeMargin = mBadgeGravity == BadgeGravity.TOP ? mBadgeMargin : 0.0F;
+ mBgBounds.set(0.0F, barBadgeMargin, mBounds.width(), mBounds.height() + barBadgeMargin);
+
+ // Set scale fraction for icons
+ for (Model model : mModels) {
+ final float originalIconSize = model.mIcon.getWidth() > model.mIcon.getHeight() ?
+ model.mIcon.getWidth() : model.mIcon.getHeight();
+ model.mInactiveIconScale = mIconSize / originalIconSize;
+ model.mActiveIconScaleBy = model.mInactiveIconScale *
+ (mIsTitled ? TITLE_ACTIVE_ICON_SCALE_BY : SCALED_FRACTION);
+ }
+
+ // Reset bitmap to init it onDraw()
+ mBitmap = null;
+ mPointerBitmap = null;
+ mIconsBitmap = null;
+ if (mIsTitled) mTitlesBitmap = null;
+
+ // Set start position of pointer for preview or on start
+ if (isInEditMode() || !mIsViewPagerMode) {
+ mIsSetIndexFromTabBar = true;
+
+ // Set random in preview mode
+ if (isInEditMode()) {
+ mIndex = new Random().nextInt(mModels.size());
+
+ if (mIsBadged)
+ for (int i = 0; i < mModels.size(); i++) {
+ final Model model = mModels.get(i);
+
+ if (i == mIndex) {
+ model.mBadgeFraction = MAX_FRACTION;
+ model.showBadge();
+ } else {
+ model.mBadgeFraction = MIN_FRACTION;
+ model.hideBadge();
+ }
+ }
+ }
+
+ mStartPointerX = mIndex * mModelSize;
+ mEndPointerX = mStartPointerX;
+ updateIndicatorPosition(MAX_FRACTION);
+ }
+
+ //The translation behavior has to be set up after the super.onMeasure has been called
+ if (!mIsBehaviorSet) {
+ setBehaviorEnabled(mBehaviorEnabled);
+ mIsBehaviorSet = true;
+ }
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ @Override
+ protected void onDraw(final Canvas canvas) {
+ // Get height of NTB with badge on nor
+ final int mBadgedHeight = (int) (mBounds.height() + mBadgeMargin);
+
+ // Set main canvas
+ if (mBitmap == null || mBitmap.isRecycled()) {
+ mBitmap = Bitmap.createBitmap(
+ (int) mBounds.width(), mBadgedHeight, Bitmap.Config.ARGB_8888
+ );
+ mCanvas.setBitmap(mBitmap);
+ }
+ // Set pointer canvas
+ if (mPointerBitmap == null || mPointerBitmap.isRecycled()) {
+ mPointerBitmap = Bitmap.createBitmap(
+ (int) mBounds.width(), mBadgedHeight, Bitmap.Config.ARGB_8888
+ );
+ mPointerCanvas.setBitmap(mPointerBitmap);
+ }
+ // Set icons canvas
+ if (mIconsBitmap == null || mIconsBitmap.isRecycled()) {
+ mIconsBitmap = Bitmap.createBitmap(
+ (int) mBounds.width(), mBadgedHeight, Bitmap.Config.ARGB_8888
+ );
+ mIconsCanvas.setBitmap(mIconsBitmap);
+ }
+ // Set titles canvas
+ if (mIsTitled) {
+ if (mTitlesBitmap == null || mTitlesBitmap.isRecycled()) {
+ mTitlesBitmap = Bitmap.createBitmap(
+ (int) mBounds.width(), mBadgedHeight, Bitmap.Config.ARGB_8888
+ );
+ mTitlesCanvas.setBitmap(mTitlesBitmap);
+ }
+ } else mTitlesBitmap = null;
+
+ // Reset and clear canvases
+ mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ mPointerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ mIconsCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ if (mIsTitled) mTitlesCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+ if (mCornersRadius == 0) canvas.drawRect(mBgBounds, mBgPaint);
+ else canvas.drawRoundRect(mBgBounds, mCornersRadius, mCornersRadius, mBgPaint);
+
+ // Get pointer badge margin for gravity
+ final float barBadgeMargin = mBadgeGravity == BadgeGravity.TOP ? mBadgeMargin : 0.0F;
+
+ // Draw our model colors
+ for (int i = 0; i < mModels.size(); i++) {
+ mPaint.setColor(mModels.get(i).getColor());
+
+ if (mIsHorizontalOrientation) {
+ final float left = mModelSize * i;
+ final float right = left + mModelSize;
+ mCanvas.drawRect(
+ left, barBadgeMargin, right, mBounds.height() + barBadgeMargin, mPaint
+ );
+ } else {
+ final float top = mModelSize * i;
+ final float bottom = top + mModelSize;
+ mCanvas.drawRect(0.0F, top, mBounds.width(), bottom, mPaint);
+ }
+ }
+
+ // Set bound of pointer
+ if (mIsHorizontalOrientation) mPointerBounds.set(
+ mPointerLeftTop,
+ barBadgeMargin,
+ mPointerRightBottom,
+ mBounds.height() + barBadgeMargin
+ );
+ 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);
+
+ // Set vars for icon when model with title or without
+ final float iconMarginTitleHeight = mIconSize + mTitleMargin + mModelTitleSize;
+
+ // Draw model icons
+ for (int i = 0; i < mModels.size(); i++) {
+ final Model model = mModels.get(i);
+
+ // Variables to center our icons
+ final float leftOffset;
+ final float topOffset;
+ final float matrixCenterX;
+ final float matrixCenterY;
+
+ // Set offset to titles
+ final float leftTitleOffset;
+ final float topTitleOffset;
+ if (mIsHorizontalOrientation) {
+ leftOffset = (mModelSize * i) + (mModelSize - model.mIcon.getWidth()) * 0.5F;
+ topOffset = (mBounds.height() - model.mIcon.getHeight()) * 0.5F;
+
+ // Set offset to titles
+ leftTitleOffset = (mModelSize * i) + (mModelSize * 0.5F);
+ topTitleOffset =
+ mBounds.height() - (mBounds.height() - iconMarginTitleHeight) * 0.5F;
+
+ } else {
+ leftOffset = (mBounds.width() - (float) model.mIcon.getWidth()) * 0.5F;
+ topOffset = (mModelSize * i) + (mModelSize - (float) model.mIcon.getHeight()) * 0.5F;
+
+ // Set offset to titles
+ leftTitleOffset = leftOffset + (float) model.mIcon.getWidth() * 0.5F;
+ topTitleOffset = topOffset + (model.mIcon.getHeight() + iconMarginTitleHeight) * 0.5f;
+ }
+
+ matrixCenterX = leftOffset + (float) model.mIcon.getWidth() * 0.5F;
+ matrixCenterY = topOffset + (float) model.mIcon.getHeight() * 0.5F;
+
+ // Title translate position
+ final float titleTranslate =
+ topOffset - model.mIcon.getHeight() * TITLE_MARGIN_SCALE_FRACTION;
+
+ // Translate icon to model center
+ model.mIconMatrix.setTranslate(
+ leftOffset,
+ (mIsTitled && mTitleMode == TitleMode.ALL) ? titleTranslate : topOffset
+ );
+
+ // Get interpolated fraction for left last and current models
+ final float interpolation =
+ mResizeInterpolator.getResizeInterpolation(mFraction, true);
+ final float lastInterpolation =
+ mResizeInterpolator.getResizeInterpolation(mFraction, false);
+
+ // Scale value relative to interpolation
+ final float matrixScale = model.mActiveIconScaleBy * interpolation;
+ final float matrixLastScale = model.mActiveIconScaleBy * lastInterpolation;
+
+ // Get title alpha relative to interpolation
+ final int titleAlpha = (int) (MAX_ALPHA * interpolation);
+ final int titleLastAlpha = MAX_ALPHA - (int) (MAX_ALPHA * lastInterpolation);
+ // Get title scale relative to interpolation
+ final float titleScale = mIsScaled ?
+ MAX_FRACTION + interpolation * TITLE_ACTIVE_SCALE_BY : MAX_FRACTION;
+ final float titleLastScale = mIsScaled ? (MAX_FRACTION + TITLE_ACTIVE_SCALE_BY) -
+ (lastInterpolation * TITLE_ACTIVE_SCALE_BY) : titleScale;
+
+ mIconPaint.setAlpha(MAX_ALPHA);
+ if (model.mSelectedIcon != null) mSelectedIconPaint.setAlpha(MAX_ALPHA);
+
+ // 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) {
+ if (mIndex == i)
+ updateCurrentModel(
+ model,
+ leftOffset,
+ topOffset,
+ titleTranslate,
+ interpolation,
+ matrixCenterX,
+ matrixCenterY,
+ matrixScale,
+ titleScale,
+ titleAlpha
+ );
+ else if (mLastIndex == i)
+ updateLastModel(
+ model,
+ leftOffset,
+ topOffset,
+ titleTranslate,
+ lastInterpolation,
+ matrixCenterX,
+ matrixCenterY,
+ matrixLastScale,
+ titleLastScale,
+ titleLastAlpha
+ );
+ else
+ updateInactiveModel(
+ model,
+ leftOffset,
+ topOffset,
+ titleScale,
+ matrixScale,
+ matrixCenterX,
+ matrixCenterY
+ );
+ } else {
+ if (i == mIndex + 1)
+ updateCurrentModel(
+ model,
+ leftOffset,
+ topOffset,
+ titleTranslate,
+ interpolation,
+ matrixCenterX,
+ matrixCenterY,
+ matrixScale,
+ titleScale,
+ titleAlpha
+ );
+ else if (i == mIndex)
+ updateLastModel(
+ model,
+ leftOffset,
+ topOffset,
+ titleTranslate,
+ lastInterpolation,
+ matrixCenterX,
+ matrixCenterY,
+ matrixLastScale,
+ titleLastScale,
+ titleLastAlpha
+ );
+ else updateInactiveModel(
+ model,
+ leftOffset,
+ topOffset,
+ titleScale,
+ matrixScale,
+ matrixCenterX,
+ matrixCenterY
+ );
+ }
+
+ // Draw original model icon
+ if (model.mSelectedIcon == null) {
+ if (model.mIcon != null && !model.mIcon.isRecycled())
+ mIconsCanvas.drawBitmap(model.mIcon, model.mIconMatrix, mIconPaint);
+ } else if (mIconPaint.getAlpha() != MIN_ALPHA &&
+ model.mIcon != null && !model.mIcon.isRecycled())
+ // Draw original icon when is visible
+ mIconsCanvas.drawBitmap(model.mIcon, model.mIconMatrix, mIconPaint);
+ // Draw selected icon when exist and visible
+ if (mSelectedIconPaint.getAlpha() != MIN_ALPHA &&
+ model.mSelectedIcon != null && !model.mSelectedIcon.isRecycled())
+ mIconsCanvas.drawBitmap(model.mSelectedIcon, model.mIconMatrix, mSelectedIconPaint);
+ if (mIsTitled) mTitlesCanvas.drawText(
+ isInEditMode() ? PREVIEW_TITLE : model.getTitle(),
+ leftTitleOffset,
+ topTitleOffset,
+ mModelTitlePaint
+ );
+ }
+
+ // Reset pointer bounds for icons and titles
+ if (mIsHorizontalOrientation)
+ mPointerBounds.set(mPointerLeftTop, 0.0F, mPointerRightBottom, mBounds.height());
+ if (mCornersRadius == 0) {
+ if (mIsTinted) mIconsCanvas.drawRect(mPointerBounds, mIconPointerPaint);
+ if (mIsTitled) mTitlesCanvas.drawRect(mPointerBounds, mIconPointerPaint);
+ } else {
+ if (mIsTinted) mIconsCanvas.drawRoundRect(
+ mPointerBounds, mCornersRadius, mCornersRadius, mIconPointerPaint
+ );
+ if (mIsTitled) mTitlesCanvas.drawRoundRect(
+ mPointerBounds, mCornersRadius, mCornersRadius, mIconPointerPaint
+ );
+ }
+
+ // Draw general bitmap
+ canvas.drawBitmap(mBitmap, 0.0F, 0.0F, null);
+ // Draw icons bitmap on top
+ canvas.drawBitmap(mIconsBitmap, 0.0F, barBadgeMargin, null);
+ // Draw titles bitmap on top
+ if (mIsTitled) canvas.drawBitmap(mTitlesBitmap, 0.0F, barBadgeMargin, null);
+
+ // If is not badged, exit
+ if (!mIsBadged) return;
+
+ // Model badge margin and offset relative to gravity mode
+ final float modelBadgeMargin =
+ mBadgeGravity == BadgeGravity.TOP ? mBadgeMargin : mBounds.height();
+ final float modelBadgeOffset =
+ mBadgeGravity == BadgeGravity.TOP ? 0.0F : mBounds.height() - mBadgeMargin;
+
+ for (int i = 0; i < mModels.size(); i++) {
+ final Model model = mModels.get(i);
+
+ // Set preview badge title
+ if (isInEditMode() || TextUtils.isEmpty(model.getBadgeTitle()))
+ model.setBadgeTitle(PREVIEW_BADGE);
+
+ // Set badge title bounds
+ mBadgePaint.setTextSize(mBadgeTitleSize * model.mBadgeFraction);
+ mBadgePaint.getTextBounds(
+ model.getBadgeTitle(), 0, model.getBadgeTitle().length(), mBadgeBounds
+ );
+
+ // Get horizontal and vertical padding for bg
+ final float horizontalPadding = mBadgeTitleSize * BADGE_HORIZONTAL_FRACTION;
+ final float verticalPadding = horizontalPadding * BADGE_VERTICAL_FRACTION;
+
+ // Set horizontal badge offset
+ final float badgeBoundsHorizontalOffset =
+ (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) {
+ mBgBadgeBounds.set(
+ badgeBoundsHorizontalOffset - badgeMargin,
+ modelBadgeMargin - badgeMargin,
+ badgeBoundsHorizontalOffset + badgeMargin,
+ modelBadgeMargin + badgeMargin
+ );
+ } else
+ mBgBadgeBounds.set(
+ 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
+ if (model.mBadgeFraction == MIN_FRACTION) mBadgePaint.setColor(Color.TRANSPARENT);
+ else mBadgePaint.setColor(mBadgeBgColor == AUTO_COLOR ? mActiveColor : mBadgeBgColor);
+ mBadgePaint.setAlpha((int) (MAX_ALPHA * model.mBadgeFraction));
+
+ // Set corners to round rect for badge bg and draw
+ 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 //noinspection ResourceAsColor
+ mBadgePaint.setColor(
+ mBadgeTitleColor == AUTO_COLOR ? 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 -
+ mBadgeBounds.bottom + modelBadgeOffset;
+ canvas.drawText(
+ model.getBadgeTitle(), badgeBoundsHorizontalOffset, badgeVerticalOffset +
+ mBadgeBounds.height() - (mBadgeBounds.height() * model.mBadgeFraction),
+ mBadgePaint
+ );
+ }
+ }
+
+ // Method to transform current fraction of NTB and position
+ protected void updateCurrentModel(
+ final Model model,
+ final float leftOffset,
+ final float topOffset,
+ final float titleTranslate,
+ final float interpolation,
+ final float matrixCenterX,
+ final float matrixCenterY,
+ final float matrixScale,
+ final float titleScale,
+ final int titleAlpha
+ ) {
+ if (mIsTitled && mTitleMode == TitleMode.ACTIVE) model.mIconMatrix.setTranslate(
+ leftOffset, topOffset - (interpolation * (topOffset - titleTranslate))
+ );
+
+ final float scale = model.mInactiveIconScale + (mIsScaled ? matrixScale : 0.0F);
+ model.mIconMatrix.postScale(scale, scale, matrixCenterX, matrixCenterY);
+
+ mModelTitlePaint.setTextSize(mModelTitleSize * titleScale);
+ if (mTitleMode == TitleMode.ACTIVE) mModelTitlePaint.setAlpha(titleAlpha);
+
+ if (model.mSelectedIcon == null) {
+ mIconPaint.setAlpha(MAX_ALPHA);
+ return;
+ }
+
+ // Calculate cross fade alpha between icon and selected icon
+ final float iconAlpha;
+ final float selectedIconAlpha;
+ if (interpolation <= 0.475F) {
+ iconAlpha = MAX_FRACTION - interpolation * 2.1F;
+ selectedIconAlpha = MIN_FRACTION;
+ } else if (interpolation >= 0.525F) {
+ iconAlpha = MIN_FRACTION;
+ selectedIconAlpha = (interpolation - 0.55F) * 1.9F;
+ } else {
+ iconAlpha = MIN_FRACTION;
+ selectedIconAlpha = MIN_FRACTION;
+ }
+
+ mIconPaint.setAlpha((int) (MAX_ALPHA * clampValue(iconAlpha)));
+ mSelectedIconPaint.setAlpha((int) (MAX_ALPHA * clampValue(selectedIconAlpha)));
+ }
+
+ // Method to transform last fraction of NTB and position
+ protected void updateLastModel(
+ final Model model,
+ final float leftOffset,
+ final float topOffset,
+ final float titleTranslate,
+ final float lastInterpolation,
+ final float matrixCenterX,
+ final float matrixCenterY,
+ final float matrixLastScale,
+ final float titleLastScale,
+ final int titleLastAlpha
+ ) {
+ if (mIsTitled && mTitleMode == TitleMode.ACTIVE) model.mIconMatrix.setTranslate(
+ leftOffset, titleTranslate + (lastInterpolation * (topOffset - titleTranslate))
+ );
+
+ final float scale = model.mInactiveIconScale +
+ (mIsScaled ? model.mActiveIconScaleBy - matrixLastScale : 0.0F);
+ model.mIconMatrix.postScale(scale, scale, matrixCenterX, matrixCenterY);
+
+ mModelTitlePaint.setTextSize(mModelTitleSize * titleLastScale);
+ if (mTitleMode == TitleMode.ACTIVE) mModelTitlePaint.setAlpha(titleLastAlpha);
+
+ if (model.mSelectedIcon == null) {
+ mIconPaint.setAlpha(MAX_ALPHA);
+ return;
+ }
+
+ // Calculate cross fade alpha between icon and selected icon
+ final float iconAlpha;
+ final float selectedIconAlpha;
+ if (lastInterpolation <= 0.475F) {
+ iconAlpha = MIN_FRACTION;
+ selectedIconAlpha = MAX_FRACTION - lastInterpolation * 2.1F;
+ } else if (lastInterpolation >= 0.525F) {
+ iconAlpha = (lastInterpolation - 0.55F) * 1.9F;
+ selectedIconAlpha = MIN_FRACTION;
+ } else {
+ iconAlpha = MIN_FRACTION;
+ selectedIconAlpha = MIN_FRACTION;
+ }
+
+ mIconPaint.setAlpha((int) (MAX_ALPHA * clampValue(iconAlpha)));
+ mSelectedIconPaint.setAlpha((int) (MAX_ALPHA * clampValue(selectedIconAlpha)));
+ }
+
+ // Method to transform others fraction of NTB and position
+ protected void updateInactiveModel(
+ final Model model,
+ final float leftOffset,
+ final float topOffset,
+ final float textScale,
+ final float matrixScale,
+ final float matrixCenterX,
+ final float matrixCenterY
+ ) {
+ if (mIsTitled && mTitleMode == TitleMode.ACTIVE)
+ model.mIconMatrix.setTranslate(leftOffset, topOffset);
+ model.mIconMatrix.postScale(
+ model.mInactiveIconScale, model.mInactiveIconScale, matrixCenterX, matrixCenterY
+ );
+
+ mModelTitlePaint.setTextSize(mModelTitleSize);
+ if (mTitleMode == TitleMode.ACTIVE) mModelTitlePaint.setAlpha(MIN_ALPHA);
+
+ // Reset icons alpha
+ if (model.mSelectedIcon == null) {
+ mIconPaint.setAlpha(MAX_ALPHA);
+ return;
+ }
+
+ mSelectedIconPaint.setAlpha(MIN_ALPHA);
+ }
+
+ protected void updateTint() {
+ if (mIsTinted) {
+ // Set color filter to wrap icons with inactive color
+ final PorterDuffColorFilter colorFilter =
+ new PorterDuffColorFilter(mInactiveColor, PorterDuff.Mode.SRC_IN);
+ mIconPaint.setColorFilter(colorFilter);
+ mSelectedIconPaint.setColorFilter(colorFilter);
+ } else {
+ // Reset active and inactive colors
+ mIconPaint.reset();
+ mSelectedIconPaint.reset();
+ }
+
+ postInvalidate();
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, final int positionOffsetPixels) {
+ if (mOnPageChangeListener != null)
+ mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
+
+ // If we animate, don`t call this
+ if (!mIsSetIndexFromTabBar) {
+ mIsResizeIn = position < mIndex;
+ mLastIndex = mIndex;
+ mIndex = position;
+
+ mStartPointerX = position * mModelSize;
+ mEndPointerX = mStartPointerX + mModelSize;
+ updateIndicatorPosition(positionOffset);
+ }
+
+ // Stop scrolling on animation end and reset values
+ if (!mAnimator.isRunning() && mIsSetIndexFromTabBar) {
+ mFraction = MIN_FRACTION;
+ mIsSetIndexFromTabBar = false;
+ }
+ }
+
+ @Override
+ public void onPageSelected(final int position) {
+ // This method is empty, because we call onPageSelected() when scroll state is idle
+ }
+
+ @Override
+ public void onPageScrollStateChanged(final int state) {
+ // If VP idle, reset to MIN_FRACTION
+ mScrollState = state;
+ if (state == ViewPager.SCROLL_STATE_IDLE) {
+ if (mOnPageChangeListener != null) mOnPageChangeListener.onPageSelected(mIndex);
+ if (mIsViewPagerMode && mOnTabBarSelectedIndexListener != null)
+ mOnTabBarSelectedIndexListener.onEndTabSelected(mModels.get(mIndex), mIndex);
+ }
+
+ if (mOnPageChangeListener != null) mOnPageChangeListener.onPageScrollStateChanged(state);
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ final SavedState savedState = (SavedState) state;
+ super.onRestoreInstanceState(savedState.getSuperState());
+ mIndex = savedState.index;
+ requestLayout();
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ final Parcelable superState = super.onSaveInstanceState();
+ final SavedState savedState = new SavedState(superState);
+ savedState.index = mIndex;
+ return savedState;
+ }
+
+ protected static class SavedState extends BaseSavedState {
+
+ private int index;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ index = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(index);
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ public static final Creator CREATOR = new Creator() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ @Override
+ protected void onConfigurationChanged(final Configuration newConfig) {
+ // Config view on rotate etc.
+ super.onConfigurationChanged(newConfig);
+ requestLayout();
+
+ // Refresh pointer and state after config changed to current
+ final int tempIndex = mIndex;
+ deselect();
+ post(new Runnable() {
+ @Override
+ public void run() {
+ setModelIndex(tempIndex, true);
+ }
+ });
+ }
+
+ // Clamp value to max and min bounds
+ protected float clampValue(final float value) {
+ return Math.max(
+ Math.min(value, NavigationTabBar.MAX_FRACTION), NavigationTabBar.MIN_FRACTION
+ );
+ }
+
+ // Hide NTB with animation
+ public void hide() {
+ if (mBehavior != null) mBehavior.hideView(this, (int) getBarHeight(), true);
+ else if (getParent() != null && getParent() instanceof CoordinatorLayout) {
+ mNeedHide = true;
+ mAnimateHide = true;
+ } else scrollDown();
+ }
+
+ // Show NTB with animation
+ public void show() {
+ if (mBehavior != null) mBehavior.resetOffset(this, true);
+ else scrollUp();
+ }
+
+ // Hide NTB or bg on scroll down
+ protected void scrollDown() {
+ ViewCompat.animate(this)
+ .translationY(getBarHeight())
+ .setInterpolator(new LinearOutSlowInInterpolator())
+ .setDuration(DEFAULT_ANIMATION_DURATION)
+ .start();
+ }
+
+ // Show NTB or bg on scroll up
+ protected void scrollUp() {
+ ViewCompat.animate(this)
+ .translationY(0.0F)
+ .setInterpolator(OUT_SLOW_IN_INTERPOLATOR)
+ .setDuration(DEFAULT_ANIMATION_DURATION)
+ .start();
+ }
+
+ // Model class
+ public static class Model {
+
+ private int mColor;
+
+ private final Bitmap mIcon;
+ private final Bitmap mSelectedIcon;
+ private final Matrix mIconMatrix = new Matrix();
+
+ private String mTitle = "";
+ private String mBadgeTitle = "";
+ private String mTempBadgeTitle = "";
+ private float mBadgeFraction;
+
+ private boolean mIsBadgeShowed;
+ private boolean mIsBadgeUpdated;
+
+ private final ValueAnimator mBadgeAnimator = new ValueAnimator();
+
+ private float mInactiveIconScale;
+ private float mActiveIconScaleBy;
+
+ Model(final Builder builder) {
+ mColor = builder.mColor;
+ mIcon = builder.mIcon;
+ mSelectedIcon = builder.mSelectedIcon;
+ mTitle = builder.mTitle;
+ mBadgeTitle = builder.mBadgeTitle;
+
+ mBadgeAnimator.addListener(new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationStart(final Animator animation) {
+ animation.removeListener(this);
+ animation.addListener(this);
+ }
+
+ @Override
+ public void onAnimationEnd(final Animator animation) {
+ animation.removeListener(this);
+ animation.addListener(this);
+
+ // Detect whether we just update text and don`t reset show state
+ if (!mIsBadgeUpdated) mIsBadgeShowed = !mIsBadgeShowed;
+ else mIsBadgeUpdated = false;
+ }
+
+ @Override
+ public void onAnimationRepeat(final Animator animation) {
+ // Change title when we update and don`t see the title
+ if (mIsBadgeUpdated) mBadgeTitle = mTempBadgeTitle;
+ }
+ });
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(final String title) {
+ mTitle = title;
+ }
+
+ public int getColor() {
+ return mColor;
+ }
+
+ public void setColor(final int color) {
+ mColor = color;
+ }
+
+ public boolean isBadgeShowed() {
+ return mIsBadgeShowed;
+ }
+
+ public String getBadgeTitle() {
+ return mBadgeTitle;
+ }
+
+ public void setBadgeTitle(final String badgeTitle) {
+ mBadgeTitle = badgeTitle;
+ }
+
+ // If your badge is visible on screen, so you can update title with animation
+ public void updateBadgeTitle(final String badgeTitle) {
+ if (!mIsBadgeShowed) return;
+ if (mBadgeAnimator.isRunning()) mBadgeAnimator.end();
+
+ mTempBadgeTitle = badgeTitle;
+ mIsBadgeUpdated = true;
+
+ mBadgeAnimator.setFloatValues(MAX_FRACTION, MIN_FRACTION);
+ mBadgeAnimator.setDuration(DEFAULT_BADGE_REFRESH_ANIMATION_DURATION);
+ mBadgeAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mBadgeAnimator.setRepeatCount(1);
+ mBadgeAnimator.start();
+ }
+
+ public void toggleBadge() {
+ if (mBadgeAnimator.isRunning()) mBadgeAnimator.end();
+ if (mIsBadgeShowed) hideBadge();
+ else showBadge();
+ }
+
+ public void showBadge() {
+ mIsBadgeUpdated = false;
+
+ if (mBadgeAnimator.isRunning()) mBadgeAnimator.end();
+ if (mIsBadgeShowed) return;
+
+ mBadgeAnimator.setFloatValues(MIN_FRACTION, MAX_FRACTION);
+ mBadgeAnimator.setInterpolator(DECELERATE_INTERPOLATOR);
+ mBadgeAnimator.setDuration(DEFAULT_BADGE_ANIMATION_DURATION);
+ mBadgeAnimator.setRepeatMode(ValueAnimator.RESTART);
+ mBadgeAnimator.setRepeatCount(0);
+ mBadgeAnimator.start();
+ }
+
+ public void hideBadge() {
+ mIsBadgeUpdated = false;
+
+ if (mBadgeAnimator.isRunning()) mBadgeAnimator.end();
+ if (!mIsBadgeShowed) return;
+
+ mBadgeAnimator.setFloatValues(MAX_FRACTION, MIN_FRACTION);
+ mBadgeAnimator.setInterpolator(ACCELERATE_INTERPOLATOR);
+ mBadgeAnimator.setDuration(DEFAULT_BADGE_ANIMATION_DURATION);
+ mBadgeAnimator.setRepeatMode(ValueAnimator.RESTART);
+ mBadgeAnimator.setRepeatCount(0);
+ mBadgeAnimator.start();
+ }
+
+ public static class Builder {
+
+ private final int mColor;
+
+ private final Bitmap mIcon;
+ private Bitmap mSelectedIcon;
+
+ private String mTitle;
+ private String mBadgeTitle;
+
+ public Builder(final Drawable icon, final int color) {
+ mColor = color;
+
+ if (icon != null) {
+ if (icon instanceof BitmapDrawable) mIcon = ((BitmapDrawable) icon).getBitmap();
+ else {
+ mIcon = Bitmap.createBitmap(
+ icon.getIntrinsicWidth(),
+ icon.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888
+ );
+ final Canvas canvas = new Canvas(mIcon);
+ icon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ icon.draw(canvas);
+ }
+ } else {
+ mIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565);
+ }
+ }
+
+ public Builder selectedIcon(final Drawable selectedIcon) {
+ if (selectedIcon != null) {
+ if (selectedIcon instanceof BitmapDrawable)
+ mSelectedIcon = ((BitmapDrawable) selectedIcon).getBitmap();
+ else {
+ mSelectedIcon = Bitmap.createBitmap(
+ selectedIcon.getIntrinsicWidth(),
+ selectedIcon.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888
+ );
+ final Canvas canvas = new Canvas(mSelectedIcon);
+ selectedIcon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ selectedIcon.draw(canvas);
+ }
+ } else mSelectedIcon = null;
+
+ return this;
+ }
+
+ public Builder title(final String title) {
+ mTitle = title;
+ return this;
+ }
+
+ public Builder badgeTitle(final String title) {
+ mBadgeTitle = title;
+ return this;
+ }
+
+ public Model build() {
+ return new Model(this);
+ }
+ }
+ }
+
+ // Custom scroller with custom scroll duration
+ protected class ResizeViewPagerScroller extends Scroller {
+
+ ResizeViewPagerScroller(Context context) {
+ super(context, new AccelerateDecelerateInterpolator());
+ }
+
+ @Override
+ public void startScroll(int startX, int startY, int dx, int dy, int duration) {
+ super.startScroll(startX, startY, dx, dy, mAnimationDuration);
+ }
+
+ @Override
+ public void startScroll(int startX, int startY, int dx, int dy) {
+ super.startScroll(startX, startY, dx, dy, mAnimationDuration);
+ }
+ }
+
+ // Resize interpolator to create smooth effect on pointer according to inspiration design
+ // This is like improved accelerated and decelerated interpolator
+ protected class ResizeInterpolator implements Interpolator {
+
+ // Spring factor
+ 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 * FACTOR));
+ else return (float) (Math.pow(input, 2.0F * FACTOR));
+ }
+
+ private float getResizeInterpolation(final float input, final boolean resizeIn) {
+ mResizeIn = resizeIn;
+ return getInterpolation(input);
+ }
+ }
+
+ // Model title mode
+ public enum TitleMode {
+ ALL, ACTIVE;
+
+ public final static int ALL_INDEX = 0;
+ public final static int ACTIVE_INDEX = 1;
+ }
+
+ // Model badge position
+ 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 final float mPositionFraction;
+
+ BadgePosition(final float positionFraction) {
+ mPositionFraction = positionFraction;
+ }
+ }
+
+ // Model badge gravity
+ public enum BadgeGravity {
+ TOP, BOTTOM;
+
+ public final static int TOP_INDEX = 0;
+ public final static int BOTTOM_INDEX = 1;
+ }
+
+ // Out listener for selected index
+ public interface OnTabBarSelectedIndexListener {
+ void onStartTabSelected(final Model model, final int index);
+
+ void onEndTabSelected(final Model model, final int index);
+ }
+}
diff --git a/library/src/main/res/values/arrays.xml b/navigationtabbar/src/main/res/values/arrays.xml
similarity index 100%
rename from library/src/main/res/values/arrays.xml
rename to navigationtabbar/src/main/res/values/arrays.xml
diff --git a/library/src/main/res/values/attrs.xml b/navigationtabbar/src/main/res/values/attrs.xml
similarity index 70%
rename from library/src/main/res/values/attrs.xml
rename to navigationtabbar/src/main/res/values/attrs.xml
index 47ec2da..0b39aa9 100644
--- a/library/src/main/res/values/attrs.xml
+++ b/navigationtabbar/src/main/res/values/attrs.xml
@@ -4,12 +4,18 @@
+
+
+
+
+
+
@@ -19,13 +25,17 @@
+
+
+
+
diff --git a/library/src/main/res/values/strings.xml b/navigationtabbar/src/main/res/values/strings.xml
similarity index 100%
rename from library/src/main/res/values/strings.xml
rename to navigationtabbar/src/main/res/values/strings.xml
diff --git a/settings.gradle b/settings.gradle
index 3306997..ee9857b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':library'
+include ':app', ':navigationtabbar'