├── navigationbarview
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── values.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── attrs.xml
│ │ │ │ └── dimens.xml
│ │ │ ├── drawable
│ │ │ │ └── item_badge_background.xml
│ │ │ └── layout
│ │ │ │ └── view_bottom_navigation_bar_item.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── jp
│ │ │ └── s64
│ │ │ └── android
│ │ │ └── navigationbarview
│ │ │ ├── view
│ │ │ ├── INavigationBarTransitionView.java
│ │ │ ├── INavigationBarItemView.java
│ │ │ ├── INavigationBarView.java
│ │ │ └── NavigationBarViewHelper.java
│ │ │ ├── item
│ │ │ ├── INavigationBarItem.java
│ │ │ ├── AbsBadgeNavigationBarItem.java
│ │ │ └── AbsNavigationBarItem.java
│ │ │ └── bottom
│ │ │ ├── BottomNavigationBarItemView.java
│ │ │ └── BottomNavigationBarView.java
│ ├── test
│ │ └── java
│ │ │ └── jp
│ │ │ └── s64
│ │ │ └── android
│ │ │ └── navigationbarview
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── jp
│ │ └── s64
│ │ └── android
│ │ └── navigationbarview
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── example
├── .gitignore
├── src
│ ├── main
│ │ ├── ic_launcher-web.png
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── ids.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_rasterized_active.png
│ │ │ │ └── ic_rasterized_inactive.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_rasterized_active.png
│ │ │ │ └── ic_rasterized_inactive.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_rasterized_active.png
│ │ │ │ └── ic_rasterized_inactive.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_rasterized_active.png
│ │ │ │ └── ic_rasterized_inactive.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_rasterized_active.png
│ │ │ │ └── ic_rasterized_inactive.png
│ │ │ ├── drawable
│ │ │ │ └── ic_account_circle_black.xml
│ │ │ └── layout
│ │ │ │ └── activity_main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── jp
│ │ │ └── s64
│ │ │ └── android
│ │ │ └── navigationbarview
│ │ │ └── example
│ │ │ └── MainActivity.java
│ ├── test
│ │ └── java
│ │ │ └── jp
│ │ │ └── s64
│ │ │ └── android
│ │ │ └── navigationbarview
│ │ │ └── example
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── jp
│ │ └── s64
│ │ └── android
│ │ └── navigationbarview
│ │ └── example
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── assets
├── header.png
├── screenshot1.png
├── screenshot2.png
├── screenshot3.png
└── screenrecord1.gif
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── README.md
├── gradlew.bat
└── gradlew
/navigationbarview/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /src/*/google-services.json
3 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':navigationbarview', ':example'
2 |
--------------------------------------------------------------------------------
/assets/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/assets/header.png
--------------------------------------------------------------------------------
/assets/screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/assets/screenshot1.png
--------------------------------------------------------------------------------
/assets/screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/assets/screenshot2.png
--------------------------------------------------------------------------------
/assets/screenshot3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/assets/screenshot3.png
--------------------------------------------------------------------------------
/assets/screenrecord1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/assets/screenrecord1.gif
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/example/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | NavigationBarView Example
3 |
4 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | NavigationBarView
3 |
4 |
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-hdpi/ic_rasterized_active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-hdpi/ic_rasterized_active.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-mdpi/ic_rasterized_active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-mdpi/ic_rasterized_active.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xhdpi/ic_rasterized_active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-xhdpi/ic_rasterized_active.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-hdpi/ic_rasterized_inactive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-hdpi/ic_rasterized_inactive.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-mdpi/ic_rasterized_inactive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-mdpi/ic_rasterized_inactive.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xhdpi/ic_rasterized_inactive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-xhdpi/ic_rasterized_inactive.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxhdpi/ic_rasterized_active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-xxhdpi/ic_rasterized_active.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxxhdpi/ic_rasterized_active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-xxxhdpi/ic_rasterized_active.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .idea
11 |
12 |
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxhdpi/ic_rasterized_inactive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-xxhdpi/ic_rasterized_inactive.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxxhdpi/ic_rasterized_inactive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/S64/android-navigation-bar-view/HEAD/example/src/main/res/mipmap-xxxhdpi/ic_rasterized_inactive.png
--------------------------------------------------------------------------------
/navigationbarview/src/main/res/values/values.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 3
5 | 5
6 |
7 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #E04747
5 | #ffffff
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Jul 14 16:03:06 JST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/res/drawable/item_badge_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #BDBDBD
8 | #19A4BA
9 | #FAFAFA
10 |
11 |
12 |
--------------------------------------------------------------------------------
/example/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/navigationbarview/src/test/java/jp/s64/android/navigationbarview/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package jp.s64.android.navigationbarview;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/example/src/test/java/jp/s64/android/navigationbarview/example/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package jp.s64.android.navigationbarview.example;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/example/src/main/res/drawable/ic_account_circle_black.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/example/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/example/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/navigationbarview/src/androidTest/java/jp/s64/android/navigationbarview/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package jp.s64.android.navigationbarview;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("jp.s64.android.navigationbarview.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/example/src/androidTest/java/jp/s64/android/navigationbarview/example/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package jp.s64.android.navigationbarview.example;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("jp.s64.android.navigationbarview.example", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/java/jp/s64/android/navigationbarview/view/INavigationBarTransitionView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Shuma Yoshioka
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jp.s64.android.navigationbarview.view;
18 |
19 | import android.animation.TimeInterpolator;
20 |
21 | public interface INavigationBarTransitionView {
22 |
23 | void setDuration(long duration);
24 |
25 | void setInterpolator(TimeInterpolator interpolator);
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/navigationbarview/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/shuma/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/java/jp/s64/android/navigationbarview/view/INavigationBarItemView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Shuma Yoshioka
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jp.s64.android.navigationbarview.view;
18 |
19 | import jp.s64.android.navigationbarview.item.INavigationBarItem;
20 |
21 | public interface INavigationBarItemView extends INavigationBarTransitionView {
22 |
23 | void setHeight(int height);
24 |
25 | void setWidth(int inactive, int active);
26 |
27 | void setItem(INavigationBarItem item);
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/example/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/shuma/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
27 | -dontwarn com.google.common.**
28 | -keep class com.google.common.** { *; }
29 | -keep interface com.google.common.** { *; }
30 |
31 | -dontwarn com.google.errorprone.**
32 | -keep class com.google.errorprone.** { *; }
33 | -keep interface com.google.errorprone.** { *; }
34 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @dimen/horizontal_height
5 |
6 | 96dp
7 | 168dp
8 |
9 | 24dp
10 |
11 | 6dp
12 | 8dp
13 |
14 | 6dp
15 | 10dp
16 | 0dp
17 |
18 | - 1.2
19 | - 1.0
20 | - 1.0
21 |
22 | 12sp
23 |
24 | 12dp
25 |
26 | 56dp
27 | 8dp
28 |
29 | 16dp
30 | 10sp
31 |
32 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/res/layout/view_bottom_navigation_bar_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
15 |
16 |
23 |
24 |
25 |
26 |
34 |
35 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/java/jp/s64/android/navigationbarview/item/INavigationBarItem.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Shuma Yoshioka
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jp.s64.android.navigationbarview.item;
18 |
19 | import android.content.Context;
20 | import android.support.annotation.ColorInt;
21 | import android.support.annotation.IdRes;
22 | import android.support.annotation.Nullable;
23 | import android.view.View;
24 |
25 | public interface INavigationBarItem {
26 |
27 | @IdRes
28 | int getIdRes();
29 |
30 | @Nullable
31 | String getText(boolean isChecked);
32 |
33 | @ColorInt
34 | int getTextColorInt(boolean isChecked);
35 |
36 | View updateIcon(@Nullable View original, Context context, int width, int height, IconAnimator animator);
37 |
38 | void onItemViewCreated(View newView);
39 |
40 | void onItemViewRemoved(View removedView);
41 |
42 | @Nullable
43 | Integer getIconPixelSize();
44 |
45 | class IconAnimator {
46 |
47 | public final boolean isChecked;
48 | public final boolean oldIsChecked;
49 | public final float fraction;
50 |
51 | public IconAnimator(boolean isChecked, boolean oldIsChecked, float fraction) {
52 | this.isChecked = isChecked;
53 | this.oldIsChecked = oldIsChecked;
54 | this.fraction = fraction;
55 | }
56 |
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NavigationBarView
2 |
3 |
4 |
5 | An example project / library of customizable Bottom Navigation
6 |
7 | 
8 |
9 |
10 |
11 | This is contains below components:
12 |
13 | - BottomNavigationBarView
14 |
15 | ## Usages
16 |
17 | Add following lines to your buildscripts.
18 |
19 | ```groovy
20 | buildscript {
21 | ext {
22 | navigation_bar_view_version = '0.0.8'
23 | }
24 | }
25 | ```
26 |
27 | ```groovy
28 | repositories {
29 | maven {
30 | url 'http://dl.bintray.com/s64/maven'
31 | }
32 | }
33 |
34 | dependencies {
35 | compile "jp.s64.android:navigationbarview:${navigation_bar_view_version}"
36 | }
37 | ```
38 |
39 | ## Apps using android-navigation-bar-view
40 |
41 | - [Wantedly Visit](https://play.google.com/store/apps/details?id=com.wantedly.android.visit) & [Intern](https://play.google.com/store/apps/details?id=com.wantedly.android.student)
42 | - [Balloon](https://play.google.com/store/apps/details?id=jp.co.fowd.balloon)
43 |
44 | ## Donate
45 |
46 |
47 |
48 | ## License
49 |
50 | ```
51 | Copyright 2017 Shuma Yoshioka
52 |
53 | Licensed under the Apache License, Version 2.0 (the "License");
54 | you may not use this file except in compliance with the License.
55 | You may obtain a copy of the License at
56 |
57 | http://www.apache.org/licenses/LICENSE-2.0
58 |
59 | Unless required by applicable law or agreed to in writing, software
60 | distributed under the License is distributed on an "AS IS" BASIS,
61 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
62 | See the License for the specific language governing permissions and
63 | limitations under the License.
64 | ```
65 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/java/jp/s64/android/navigationbarview/view/INavigationBarView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Shuma Yoshioka
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jp.s64.android.navigationbarview.view;
18 |
19 | import android.support.annotation.IdRes;
20 | import android.support.annotation.Nullable;
21 | import android.view.View;
22 |
23 | import jp.s64.android.navigationbarview.item.INavigationBarItem;
24 |
25 | public interface INavigationBarView extends INavigationBarTransitionView {
26 |
27 | void clearItems();
28 |
29 | void add(INavigationBarItem... items);
30 |
31 | void remove(INavigationBarItem... items);
32 |
33 | void remove(int index);
34 |
35 | void replace(int index, INavigationBarItem item);
36 |
37 | int size();
38 |
39 | void setItemLimit(@Nullable Integer min, @Nullable Integer max);
40 |
41 | int getMaxSize();
42 |
43 | int getMinSize();
44 |
45 | void check(@IdRes int idRes);
46 |
47 | void uncheck();
48 |
49 | @Nullable
50 | View getItemView(@IdRes int idRes);
51 |
52 | void setOnCheckChangedListener(@Nullable OnCheckChangeListener listener);
53 |
54 | void show(OnVisibilityAnimateListener doAnimate);
55 |
56 | void hide(OnVisibilityAnimateListener doAnimate);
57 |
58 | boolean isNavigationBarShown();
59 |
60 | @IdRes
61 | int getChecked();
62 |
63 | interface OnCheckChangeListener {
64 |
65 | void onCheckChanged(@IdRes int oldIdRes, @IdRes int newIdRes);
66 |
67 | }
68 |
69 | interface OnVisibilityAnimateListener {
70 |
71 | void onVisibilityAnimate(SELF self);
72 |
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/example/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'com.google.firebase.firebase-crash'
3 |
4 | android {
5 | compileSdkVersion android_compilesdk_version
6 | buildToolsVersion android_buildtools_version
7 |
8 | configurations.all {
9 | resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
10 | }
11 |
12 | defaultConfig {
13 | applicationId "jp.s64.android.navigationbarview.example"
14 | minSdkVersion android_minsdk_version
15 | targetSdkVersion android_targetsdk_version
16 | versionCode android_version_code
17 | versionName navigation_bar_view_version_name
18 |
19 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
20 |
21 | }
22 |
23 | signingConfigs {
24 |
25 | /*
26 | debug {
27 | storeFile System.getenv('ANDROID_DEBUG_KEYSTORE_FILE') != null ? file(System.getenv('ANDROID_DEBUG_KEYSTORE_FILE')) : rootProject.file('debug.keystore')
28 | storePassword 'android'
29 | keyAlias 'androiddebugkey'
30 | keyPassword 'android'
31 | }
32 | */
33 |
34 | release {
35 | storeFile file(release_keystore_file)
36 | storePassword release_keystore_password
37 | keyAlias release_key_alias
38 | keyPassword release_key_password
39 | }
40 |
41 | }
42 |
43 | buildTypes {
44 |
45 | debug {
46 | minifyEnabled false
47 | debuggable true
48 | zipAlignEnabled true
49 | shrinkResources false
50 | //signingConfig signingConfigs.debug
51 |
52 | applicationIdSuffix '.debug'
53 | versionNameSuffix '-debug'
54 | }
55 |
56 | release {
57 | minifyEnabled true
58 | debuggable false
59 | zipAlignEnabled true
60 | shrinkResources true
61 | signingConfig signingConfigs.release
62 |
63 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
64 | }
65 |
66 | }
67 |
68 | }
69 |
70 | dependencies {
71 | compile fileTree(dir: 'libs', include: ['*.jar'])
72 |
73 | androidTestCompile("com.android.support.test.espresso:espresso-core:${espresso_version}", {
74 | exclude group: 'com.android.support', module: 'support-annotations'
75 | })
76 |
77 | testCompile "junit:junit:${junit_version}"
78 | compile "com.google.firebase:firebase-crash:${firebase_library_version}"
79 |
80 | compile project(':navigationbarview')
81 | }
82 |
83 | apply plugin: 'com.google.gms.google-services'
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/navigationbarview/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | apply plugin: 'com.jfrog.bintray'
4 |
5 | group = artifact_group
6 | version = navigation_bar_view_version_name
7 |
8 | android {
9 | compileSdkVersion android_compilesdk_version
10 | buildToolsVersion android_buildtools_version
11 |
12 | defaultConfig {
13 | minSdkVersion android_minsdk_version
14 | targetSdkVersion android_targetsdk_version
15 | versionCode android_version_code
16 | versionName navigation_bar_view_version_name
17 |
18 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
19 |
20 | }
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 | }
28 |
29 | dependencies {
30 | compile fileTree(dir: 'libs', include: ['*.jar'])
31 | androidTestCompile("com.android.support.test.espresso:espresso-core:${espresso_version}", {
32 | exclude group: 'com.android.support', module: 'support-annotations'
33 | })
34 | compile "com.android.support:appcompat-v7:${android_support_library_version}"
35 | compile "com.android.support:design:${android_support_library_version}"
36 |
37 | compile "com.google.guava:guava:${guava_version}"
38 | compile "jp.s64.android.radiobuttonextended:core:${radiobutton_extended_version}"
39 |
40 | testCompile "junit:junit:${junit_version}"
41 | }
42 |
43 | bintray {
44 | user = bintray_user
45 | key = bintray_api_key
46 |
47 | configurations = default_configurations
48 |
49 | pkg {
50 | repo = default_repo
51 | name = 'android-navigation-bar-view'
52 | licenses = default_licenses
53 | websiteUrl = site_url
54 | issueTrackerUrl = issues_url
55 | vcsUrl = git_url
56 | labels = shared_labels
57 | publicDownloadNumbers = true
58 |
59 | version {
60 | name = navigation_bar_view_version_name
61 | released = toIsoDateFormat(new Date())
62 | vcsTag = navigation_bar_view_version_name
63 | }
64 | }
65 |
66 | }
67 |
68 | install {
69 | repositories.mavenInstaller {
70 | pom {
71 | project {
72 | packaging 'aar'
73 | scm {
74 | url site_url
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
81 | task sourcesJar(type: Jar) {
82 | classifier = 'sources'
83 | from android.sourceSets.main.java.srcDirs
84 | }
85 |
86 | task javadoc(type: Javadoc) {
87 | source = android.sourceSets.main.java.srcDirs
88 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
89 | }
90 |
91 | task javadocJar(type: Jar, dependsOn: javadoc) {
92 | classifier = 'javadoc'
93 | from javadoc.destinationDir
94 | }
95 |
96 | artifacts {
97 | archives sourcesJar, javadocJar
98 | }
--------------------------------------------------------------------------------
/navigationbarview/src/main/java/jp/s64/android/navigationbarview/item/AbsBadgeNavigationBarItem.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Shuma Yoshioka
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jp.s64.android.navigationbarview.item;
18 |
19 | import android.content.Context;
20 | import android.graphics.Typeface;
21 | import android.support.annotation.Nullable;
22 | import android.support.v4.content.ContextCompat;
23 | import android.support.v4.content.res.ResourcesCompat;
24 | import android.support.v4.view.ViewCompat;
25 | import android.support.v7.widget.AppCompatTextView;
26 | import android.util.TypedValue;
27 | import android.view.Gravity;
28 | import android.view.View;
29 | import android.widget.RelativeLayout;
30 | import android.widget.TextView;
31 |
32 | import jp.s64.android.navigationbarview.R;
33 |
34 | public abstract class AbsBadgeNavigationBarItem extends AbsNavigationBarItem {
35 |
36 | protected AppCompatTextView mBadge;
37 |
38 | protected CharSequence mBadgeText;
39 |
40 | @Override
41 | public View updateIcon(@Nullable View original, Context context, int width, int height, IconAnimator animator) {
42 | RelativeLayout ret = (RelativeLayout) super.updateIcon(original, context, width, height, animator);
43 | AppCompatTextView badge = null;
44 | {
45 | for (int i = 0; i < ret.getChildCount(); i++) {
46 | View v = ret.getChildAt(i);
47 | if (v instanceof TextView) {
48 | badge = (AppCompatTextView) v;
49 | break;
50 | }
51 | }
52 | }
53 | if (badge == null) {
54 | CharSequence oldText;
55 | if (mBadgeText != null) {
56 | oldText = mBadgeText;
57 | mBadgeText = null;
58 | } else {
59 | oldText = mBadge != null ? mBadge.getText() : null;
60 | }
61 | badge = mBadge = new AppCompatTextView(context);
62 | int size = context.getResources().getDimensionPixelSize(R.dimen.item_badge_size);
63 | {
64 | RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(size, size);
65 | params.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
66 | params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
67 | params.topMargin = (size / 4) * -1;
68 | params.rightMargin = (size / 3) * -1;
69 | badge.setLayoutParams(params);
70 | badge.setIncludeFontPadding(false);
71 | badge.setGravity(Gravity.CENTER);
72 | }
73 | {
74 | badge.setTextColor(ContextCompat.getColor(context, R.color.item_badge_text_color));
75 | ViewCompat.setBackground(badge, ResourcesCompat.getDrawable(context.getResources(), R.drawable.item_badge_background, context.getTheme()));
76 | badge.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.item_badge_text_size));
77 | badge.setTypeface(null, Typeface.BOLD);
78 | }
79 | {
80 | setBadgeText(oldText);
81 | }
82 | ret.addView(badge);
83 | }
84 | return ret;
85 | }
86 |
87 | public void setBadgeText(@Nullable CharSequence text) {
88 | if (mBadge != null) {
89 | mBadge.setText(text);
90 | mBadge.setAlpha(text != null ? 1f : 0f);
91 | } else {
92 | mBadgeText = text;
93 | }
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/java/jp/s64/android/navigationbarview/item/AbsNavigationBarItem.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Shuma Yoshioka
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jp.s64.android.navigationbarview.item;
18 |
19 | import android.animation.ArgbEvaluator;
20 | import android.content.Context;
21 | import android.graphics.PorterDuff;
22 | import android.graphics.drawable.Drawable;
23 | import android.support.annotation.ColorInt;
24 | import android.support.annotation.DrawableRes;
25 | import android.support.annotation.Nullable;
26 | import android.support.v4.graphics.drawable.DrawableCompat;
27 | import android.support.v7.widget.AppCompatImageView;
28 | import android.view.View;
29 | import android.view.ViewGroup;
30 | import android.widget.ImageView;
31 | import android.widget.RelativeLayout;
32 |
33 | public abstract class AbsNavigationBarItem implements INavigationBarItem {
34 |
35 | @Override
36 | public View updateIcon(@Nullable View original, Context context, int width, int height, IconAnimator animator) {
37 | RelativeLayout ret;
38 | AppCompatImageView img;
39 | if (original == null) {
40 | ret = new RelativeLayout(context);
41 | {
42 | ret.setLayoutParams(new ViewGroup.LayoutParams(width, height));
43 | }
44 | img = new AppCompatImageView(context);
45 | RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
46 | {
47 | img.setLayoutParams(params);
48 | img.setScaleType(ImageView.ScaleType.FIT_CENTER);
49 | }
50 | ret.addView(img);
51 | } else {
52 | ret = (RelativeLayout) original;
53 | img = (AppCompatImageView) ret.getChildAt(0);
54 | }
55 | {
56 | img.setImageResource(getDrawableIdRes(animator.isChecked));
57 | }
58 | Integer colorInt;
59 | if (animator.isChecked != animator.oldIsChecked) {
60 | ArgbEvaluator ev = new ArgbEvaluator();
61 | Integer before, after;
62 | {
63 | before = getColorInt(animator.oldIsChecked);
64 | after = getColorInt(animator.isChecked);
65 | }
66 | if (before != null && after != null) {
67 | colorInt = (int) ev.evaluate(
68 | animator.fraction,
69 | before,
70 | after
71 | );
72 | } else {
73 | colorInt = null;
74 | }
75 | } else {
76 | colorInt = getColorInt(animator.isChecked);
77 | }
78 | {
79 | Drawable d = DrawableCompat.wrap(img.getDrawable()).mutate();
80 | if (colorInt != null) {
81 | DrawableCompat.setTint(d, colorInt);
82 | DrawableCompat.setTintMode(d, PorterDuff.Mode.SRC_IN);
83 | } else {
84 | DrawableCompat.setTintList(d, null);
85 | }
86 | img.setImageDrawable(d);
87 | }
88 | return ret;
89 | }
90 |
91 | @Override
92 | public void onItemViewCreated(View newView) {
93 | // no-op
94 | }
95 |
96 | @Override
97 | public void onItemViewRemoved(View removedView) {
98 | // no-op
99 | }
100 |
101 | @DrawableRes
102 | public abstract int getDrawableIdRes(boolean isChecked);
103 |
104 | @Nullable
105 | @ColorInt
106 | public abstract Integer getColorInt(boolean isChecked);
107 |
108 | @Nullable
109 | @Override
110 | public Integer getIconPixelSize() {
111 | return null;
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
16 |
17 |
25 |
26 |
31 |
32 |
38 |
39 |
45 |
46 |
47 |
48 |
53 |
54 |
58 |
59 |
66 |
67 |
68 |
69 |
75 |
76 |
77 |
78 |
87 |
88 |
94 |
95 |
101 |
102 |
103 |
104 |
113 |
114 |
120 |
121 |
127 |
128 |
129 |
130 |
139 |
140 |
146 |
147 |
153 |
154 |
155 |
156 |
165 |
166 |
172 |
173 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
192 |
193 |
194 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/java/jp/s64/android/navigationbarview/view/NavigationBarViewHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Shuma Yoshioka
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jp.s64.android.navigationbarview.view;
18 |
19 | import android.animation.TimeInterpolator;
20 | import android.support.annotation.IdRes;
21 | import android.support.annotation.Nullable;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.Checkable;
25 |
26 | import com.google.common.collect.ImmutableList;
27 | import com.google.common.collect.Lists;
28 |
29 | import java.util.ArrayList;
30 | import java.util.LinkedHashSet;
31 | import java.util.LinkedList;
32 | import java.util.List;
33 | import java.util.Locale;
34 |
35 | import jp.s64.android.navigationbarview.R;
36 | import jp.s64.android.navigationbarview.item.INavigationBarItem;
37 | import jp.s64.android.radiobuttonextended.core.widget.CompoundFrameLayoutRadioGroup;
38 |
39 | public class NavigationBarViewHelper, ITEM extends View & Checkable & INavigationBarItemView> implements INavigationBarView {
40 |
41 | private final LinkedHashSet mItems = new LinkedHashSet<>();
42 | private final SELF self;
43 |
44 | private final View.OnLayoutChangeListener mLayoutChangeListener = new View.OnLayoutChangeListener() {
45 | @Override
46 | public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
47 | if (self.getVisibility() == View.VISIBLE) {
48 | assertMoreThanMinSize(self.size());
49 | assertLessThanMaxSize(self.size());
50 | }
51 | }
52 | };
53 |
54 | @Nullable
55 | private Integer mDefinedMaxSize = null;
56 |
57 | @Nullable
58 | private Integer mDefinedMinSize = null;
59 |
60 | @Nullable
61 | private OnCheckChangeListener mListener = null;
62 |
63 | @Nullable
64 | private Integer mOldCheckedId = null;
65 |
66 | public NavigationBarViewHelper(SELF self) {
67 | {
68 | this.self = self;
69 | }
70 | setItemLimit(null, null);
71 | self.addOnLayoutChangeListener(mLayoutChangeListener);
72 | }
73 |
74 | @Override
75 | public void clearItems() {
76 | ImmutableList oldItems = ImmutableList.copyOf(mItems),
77 | newItems = ImmutableList.of();
78 | {
79 | mItems.clear();
80 | }
81 | self.onItemsChanged(oldItems, newItems);
82 | }
83 |
84 | @Override
85 | public void add(INavigationBarItem... items) {
86 | ImmutableList oldItems, newItems;
87 | {
88 | oldItems = ImmutableList.copyOf(mItems);
89 | }
90 | for (INavigationBarItem item : items) {
91 | mItems.add(item);
92 | }
93 | {
94 | newItems = ImmutableList.copyOf(mItems);
95 | }
96 | self.onItemsChanged(oldItems, newItems);
97 | }
98 |
99 | @Override
100 | public void remove(INavigationBarItem... items) {
101 | ImmutableList oldItems, newItems;
102 | {
103 | oldItems = ImmutableList.copyOf(mItems);
104 | }
105 | for (INavigationBarItem item : items) {
106 | mItems.remove(item);
107 | }
108 | {
109 | newItems = ImmutableList.copyOf(mItems);
110 | }
111 | self.onItemsChanged(oldItems, newItems);
112 | }
113 |
114 | @Override
115 | public void remove(int index) {
116 | remove(Lists.newArrayList(mItems).get(index));
117 | }
118 |
119 | @Override
120 | public void replace(int index, INavigationBarItem item) {
121 | ImmutableList oldItems;
122 | List newItems;
123 | {
124 | oldItems = ImmutableList.copyOf(mItems);
125 | }
126 | {
127 | newItems = new ArrayList<>(oldItems);
128 | newItems.remove(index);
129 | newItems.add(index, item);
130 | }
131 | {
132 | self.onItemsChanged(oldItems, ImmutableList.copyOf(newItems));
133 | }
134 | }
135 |
136 | @Override
137 | public int size() {
138 | return mItems.size();
139 | }
140 |
141 | @Override
142 | public void setItemLimit(@Nullable Integer min, @Nullable Integer max) {
143 | mDefinedMaxSize = max != null ? max : self.getResources().getInteger(R.integer.default_max_items);
144 | mDefinedMinSize = min != null ? min : self.getResources().getInteger(R.integer.default_min_items);
145 | }
146 |
147 | @Override
148 | public int getMaxSize() {
149 | return mDefinedMaxSize != null ? mDefinedMaxSize : Integer.MAX_VALUE;
150 | }
151 |
152 | @Override
153 | public int getMinSize() {
154 | return mDefinedMinSize != null ? mDefinedMinSize : 0;
155 | }
156 |
157 | @Deprecated
158 | @Override
159 | public void check(@IdRes int idRes) {
160 | throw new UnsupportedOperationException();
161 | }
162 |
163 | @Deprecated
164 | @Override
165 | public void uncheck() {
166 | throw new UnsupportedOperationException();
167 | }
168 |
169 | @Deprecated
170 | @Nullable
171 | @Override
172 | public View getItemView(@IdRes int idRes) {
173 | throw new UnsupportedOperationException();
174 | }
175 |
176 | @Override
177 | public void setOnCheckChangedListener(@Nullable OnCheckChangeListener listener) {
178 | mListener = listener;
179 | }
180 |
181 | @Deprecated
182 | @Override
183 | public void show(OnVisibilityAnimateListener doAnimate) {
184 | throw new UnsupportedOperationException();
185 | }
186 |
187 | @Deprecated
188 | @Override
189 | public void hide(OnVisibilityAnimateListener doAnimate) {
190 | throw new UnsupportedOperationException();
191 | }
192 |
193 | @Deprecated
194 | @Override
195 | public boolean isNavigationBarShown() {
196 | throw new UnsupportedOperationException();
197 | }
198 |
199 | @Deprecated
200 | @Override
201 | public int getChecked() {
202 | throw new UnsupportedOperationException();
203 | }
204 |
205 | public void onItemsChanged(CompoundFrameLayoutRadioGroup container, ImmutableList oldItems, ImmutableList newItems) {
206 | assertLessThanMaxSize(newItems.size());
207 | //assertMoreThanMinSize(newItems.size());
208 |
209 | final int max;
210 | {
211 | int nc = newItems.size(), oc = oldItems.size();
212 | max = nc > oc ? nc : oc;
213 | }
214 |
215 | final List pendingRemoveViews = new LinkedList<>();
216 |
217 | for (int i = 0; i < max; i++) {
218 | INavigationBarItem newItem;
219 | {
220 | newItem = newItems.size() > i ? newItems.get(i) : null;
221 | }
222 | if (newItem == null) {
223 | pendingRemoveViews.add(container.getChildAt(i));
224 | } else if (oldItems.indexOf(newItem) != i) {
225 | View newView = self.createItemView(newItem, container.getCheckedRadioButtonId() == newItem.getIdRes()),
226 | placedView = container.getChildAt(i);
227 | {
228 | container.addView(newView, i);
229 | newItem.onItemViewCreated(newView);
230 | }
231 | if (placedView != null) {
232 | container.removeView(placedView);
233 | newItem.onItemViewRemoved(placedView);
234 | }
235 | }
236 | }
237 | for (View pendingRemoveView : pendingRemoveViews) {
238 | container.removeView(pendingRemoveView);
239 | }
240 | }
241 |
242 | protected void assertLessThanMaxSize(int newSize) {
243 | if (newSize <= getMaxSize()) {
244 | return;
245 | }
246 | throw new ItemsOutOfLimitException(getMinSize(), getMaxSize());
247 | }
248 |
249 | protected void assertMoreThanMinSize(int newSize) {
250 | if (newSize >= getMinSize()) {
251 | return;
252 | }
253 | throw new ItemsOutOfLimitException(getMinSize(), getMaxSize());
254 | }
255 |
256 | @Deprecated
257 | @Override
258 | public void setDuration(long duration) {
259 | throw new UnsupportedOperationException();
260 | }
261 |
262 | @Deprecated
263 | @Override
264 | public void setInterpolator(TimeInterpolator interpolator) {
265 | throw new UnsupportedOperationException();
266 | }
267 |
268 | public void onCheckChanged(@IdRes int checkedId) {
269 | if (mListener != null) {
270 | mListener.onCheckChanged(
271 | mOldCheckedId == null ? View.NO_ID : mOldCheckedId,
272 | checkedId
273 | );
274 | }
275 | mOldCheckedId = checkedId;
276 | }
277 |
278 | public static class ItemsOutOfLimitException extends RuntimeException {
279 |
280 | protected static final String FORMAT = "Number of items supported by BottomNavigationView is %d - %d.";
281 |
282 | public ItemsOutOfLimitException(int min, int max) {
283 | super(String.format(
284 | Locale.ROOT,
285 | FORMAT,
286 | min,
287 | max
288 | ));
289 | }
290 |
291 | }
292 |
293 | public interface IListener- {
294 |
295 | void onItemsChanged(ImmutableList oldItems, ImmutableList newItems);
296 |
297 | ITEM createItemView(INavigationBarItem item, boolean isChecked);
298 |
299 | }
300 |
301 | }
302 |
--------------------------------------------------------------------------------
/example/src/main/java/jp/s64/android/navigationbarview/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Shuma Yoshioka
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jp.s64.android.navigationbarview.example;
18 |
19 | import android.content.Context;
20 | import android.os.Bundle;
21 | import android.support.annotation.IdRes;
22 | import android.support.annotation.Nullable;
23 | import android.support.design.widget.CoordinatorLayout;
24 | import android.support.v4.content.ContextCompat;
25 | import android.support.v7.app.AppCompatActivity;
26 | import android.view.View;
27 | import android.view.ViewGroup;
28 | import android.widget.Button;
29 | import android.widget.EditText;
30 | import android.widget.RadioButton;
31 | import android.widget.RadioGroup;
32 | import android.widget.Toast;
33 |
34 | import java.util.HashMap;
35 | import java.util.Locale;
36 | import java.util.Map;
37 |
38 | import jp.s64.android.navigationbarview.bottom.BottomNavigationBarView;
39 | import jp.s64.android.navigationbarview.item.AbsBadgeNavigationBarItem;
40 | import jp.s64.android.navigationbarview.item.INavigationBarItem;
41 | import jp.s64.android.navigationbarview.view.INavigationBarView;
42 |
43 | public class MainActivity extends AppCompatActivity implements View.OnClickListener, INavigationBarView.OnCheckChangeListener, RadioGroup.OnCheckedChangeListener {
44 |
45 | private final Map INDEX_ID_PAIRS = new HashMap<>();
46 | private final Map ID_ITEM_PAIRS = new HashMap<>();
47 |
48 | private BottomNavigationBarView mNavigation;
49 | private Button mAdd, mRemove, mUncheck;
50 |
51 | private RadioGroup mTextModeGroup;
52 | private RadioGroup mWeightModeGroup;
53 |
54 | private RadioGroup mBehaviorModeGroup;
55 | private RadioGroup mLayoutModeGroup;
56 |
57 | private RadioGroup mIconModeGroup;
58 |
59 | private boolean mIconModeIsTintable = true;
60 |
61 | @Override
62 | protected void onCreate(Bundle savedInstanceState) {
63 | {
64 | INDEX_ID_PAIRS.put(0, R.id.menu_item_0);
65 | INDEX_ID_PAIRS.put(1, R.id.menu_item_1);
66 | INDEX_ID_PAIRS.put(2, R.id.menu_item_2);
67 | INDEX_ID_PAIRS.put(3, R.id.menu_item_3);
68 | INDEX_ID_PAIRS.put(4, R.id.menu_item_4);
69 | }
70 | super.onCreate(savedInstanceState);
71 | setContentView(R.layout.activity_main);
72 | {
73 | mNavigation = (BottomNavigationBarView) findViewById(R.id.target_view);
74 | }
75 | {
76 | mAdd = (Button) findViewById(R.id.add);
77 | mRemove = (Button) findViewById(R.id.remove);
78 | mUncheck = (Button) findViewById(R.id.uncheck_all);
79 | }
80 | {
81 | mTextModeGroup = (RadioGroup) findViewById(R.id.text_mode_group);
82 | mWeightModeGroup = (RadioGroup) findViewById(R.id.weight_mode_group);
83 | }
84 | {
85 | mBehaviorModeGroup = (RadioGroup) findViewById(R.id.behavior_mode_group);
86 | mLayoutModeGroup = (RadioGroup) findViewById(R.id.layout_mode_group);
87 | }
88 | {
89 | mIconModeGroup = (RadioGroup) findViewById(R.id.icon_mode_group);
90 | }
91 | for (MyItem.TextMode mode : MyItem.TextMode.values()) {
92 | RadioButton radio = new RadioButton(this);
93 | {
94 | radio.setId(mode.radioIdRes);
95 | radio.setText(mode.name().toLowerCase());
96 | }
97 | {
98 | RadioGroup.LayoutParams params = new RadioGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
99 | params.weight = 1;
100 | radio.setLayoutParams(params);
101 | }
102 | mTextModeGroup.addView(radio);
103 | }
104 | {
105 | mAdd.setOnClickListener(this);
106 | mRemove.setOnClickListener(this);
107 | mUncheck.setOnClickListener(this);
108 | }
109 | {
110 | mTextModeGroup.setOnCheckedChangeListener(this);
111 | mTextModeGroup.check(MyItem.TextMode.ONLY_ACTIVE.radioIdRes);
112 | }
113 | {
114 | mWeightModeGroup.setOnCheckedChangeListener(this);
115 | mWeightModeGroup.check(R.id.weight_mode_flexible);
116 | }
117 | {
118 | mBehaviorModeGroup.setOnCheckedChangeListener(this);
119 | mBehaviorModeGroup.check(R.id.behavior_mode_behavior);
120 | }
121 | {
122 | mLayoutModeGroup.setOnCheckedChangeListener(this);
123 | mBehaviorModeGroup.check(R.id.layout_mode_show);
124 | }
125 | {
126 | mIconModeGroup.setOnCheckedChangeListener(this);
127 | mIconModeGroup.check(R.id.icon_mode_tintable);
128 | }
129 | {
130 | mNavigation.setItemLimit(0, null);
131 | mNavigation.setOnCheckChangedListener(this);
132 | }
133 | {
134 | resetButtonState();
135 | }
136 | }
137 |
138 | @Override
139 | public void onClick(View v) {
140 | if (v == mAdd) {
141 | mNavigation.add(createItem());
142 | } else if (v == mRemove) {
143 | mNavigation.remove(mNavigation.size() - 1);
144 | } else if (v == mUncheck) {
145 | mNavigation.uncheck();
146 | }
147 | {
148 | resetButtonState();
149 | }
150 | }
151 |
152 | private void refreshItems() {
153 | for (int i = 0; i < mNavigation.size(); i++) {
154 | mNavigation.replace(i, createItem(i));
155 | }
156 | resetButtonState();
157 | }
158 |
159 | protected void resetButtonState() {
160 | {
161 | mAdd.setEnabled(mNavigation.size() < mNavigation.getMaxSize());
162 | mRemove.setEnabled(mNavigation.size() > mNavigation.getMinSize());
163 | }
164 | }
165 |
166 | protected INavigationBarItem createItem() {
167 | return createItem(mNavigation.size());
168 | }
169 |
170 | protected INavigationBarItem createItem(int i) {
171 | MyItem.TextMode textMode = MyItem.TextMode.ONLY_ACTIVE;
172 | for (MyItem.TextMode mode : MyItem.TextMode.values()) {
173 | if (mode.radioIdRes == mTextModeGroup.getCheckedRadioButtonId()) {
174 | textMode = mode;
175 | }
176 | }
177 | final int id = INDEX_ID_PAIRS.get(i);
178 | final Integer iconPixelSize;
179 | {
180 | EditText input = (EditText) findViewById(R.id.icon_size_input);
181 | String str = input.getText().toString();
182 | if (str != null && str.length() > 0) {
183 | iconPixelSize = Integer.valueOf(str);
184 | } else {
185 | iconPixelSize = null;
186 | }
187 | }
188 | MyItem ret = new MyItem(this, id, textMode) {
189 |
190 | @Nullable
191 | @Override
192 | public Integer getIconPixelSize() {
193 | return iconPixelSize;
194 | }
195 |
196 | @Override
197 | public int getDrawableIdRes(boolean isChecked) {
198 | if (mIconModeIsTintable)
199 | return R.drawable.ic_account_circle_black;
200 | else
201 | return isChecked ? R.mipmap.ic_rasterized_active : R.mipmap.ic_rasterized_inactive;
202 | }
203 |
204 | @Nullable
205 | @Override
206 | public Integer getColorInt(boolean isChecked) {
207 | if (mIconModeIsTintable)
208 | return getMyColor(isChecked);
209 | else
210 | return null;
211 | }
212 |
213 | };
214 | {
215 | ret.setBadgeText(String.valueOf(1));
216 | }
217 | ID_ITEM_PAIRS.put(id, ret);
218 | return ret;
219 | }
220 |
221 | @Override
222 | public void onCheckChanged(@IdRes int oldIdRes, @IdRes int newIdRes) {
223 | Toast.makeText(
224 | this,
225 | String.format(Locale.ROOT, "%d -> %d", oldIdRes, newIdRes),
226 | Toast.LENGTH_SHORT
227 | ).show();
228 | if (ID_ITEM_PAIRS.containsKey(newIdRes)) {
229 | ID_ITEM_PAIRS.get(newIdRes).setBadgeText(null);
230 | }
231 | }
232 |
233 | @Override
234 | public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
235 | if (group == mWeightModeGroup) {
236 | mNavigation.setItemWidthFixed(checkedId == R.id.weight_mode_fixed);
237 | } else if (group == mBehaviorModeGroup) {
238 | CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) mNavigation.getLayoutParams();
239 | switch (checkedId) {
240 | case R.id.behavior_mode_behavior:
241 | params.setBehavior(new BottomNavigationBarView.LiftUpBottomBehavior());
242 | break;
243 | case R.id.behavior_mode_layout:
244 | default:
245 | params.setBehavior(null);
246 | break;
247 | }
248 | mNavigation.setLayoutParams(params);
249 | mLayoutModeGroup.check(R.id.layout_mode_show);
250 | } else if (group == mLayoutModeGroup) {
251 | switch (checkedId) {
252 | case R.id.layout_mode_show:
253 | mNavigation.show(new INavigationBarView.OnVisibilityAnimateListener() {
254 | @Override
255 | public void onVisibilityAnimate(SELF self) {
256 | // no-op
257 | }
258 | });
259 | break;
260 | case R.id.layout_mode_hide:
261 | default:
262 | mNavigation.hide(new INavigationBarView.OnVisibilityAnimateListener() {
263 | @Override
264 | public void onVisibilityAnimate(SELF self) {
265 | // no-op
266 | }
267 | });
268 | break;
269 | }
270 | } else if (group == mIconModeGroup) {
271 | switch (checkedId) {
272 | case R.id.icon_mode_rasterized:
273 | mIconModeIsTintable = false;
274 | break;
275 | case R.id.icon_mode_tintable:
276 | default:
277 | mIconModeIsTintable = true;
278 | break;
279 | }
280 | refreshItems();
281 | }
282 | }
283 |
284 | public static abstract class MyItem extends AbsBadgeNavigationBarItem {
285 |
286 | private final Context context;
287 |
288 | @IdRes
289 | private final int idRes;
290 |
291 | private final TextMode mode;
292 |
293 | public enum TextMode {
294 | ALWAYS(R.id.text_mode_always),
295 | ONLY_ACTIVE(R.id.text_mode_only_active),
296 | DISABLE(R.id.text_mode_disable),
297 | //
298 | ;
299 |
300 | @IdRes
301 | public final int radioIdRes;
302 |
303 | TextMode(@IdRes int radioIdRes) {
304 | this.radioIdRes = radioIdRes;
305 | }
306 |
307 | }
308 |
309 | public MyItem(Context context, @IdRes int idRes, TextMode mode) {
310 | this.context = context;
311 | this.idRes = idRes;
312 | this.mode = mode;
313 | }
314 |
315 | @Override
316 | public int getIdRes() {
317 | return idRes;
318 | }
319 |
320 | @Nullable
321 | @Override
322 | public String getText(boolean isChecked) {
323 | switch (mode) {
324 | case ALWAYS:
325 | return "Always";
326 | case ONLY_ACTIVE:
327 | return isChecked ? "Active" : null;
328 | case DISABLE:
329 | default:
330 | return null;
331 | }
332 | }
333 |
334 | @Override
335 | public int getTextColorInt(boolean isChecked) {
336 | return getMyColor(isChecked);
337 | }
338 |
339 | protected Integer getMyColor(boolean isChecked) {
340 | return ContextCompat.getColor(
341 | context,
342 | isChecked ? R.color.menu_enabled : R.color.menu_disabled
343 | );
344 | }
345 |
346 | }
347 |
348 | }
349 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/java/jp/s64/android/navigationbarview/bottom/BottomNavigationBarItemView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Shuma Yoshioka
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jp.s64.android.navigationbarview.bottom;
18 |
19 | import android.animation.Animator;
20 | import android.animation.ArgbEvaluator;
21 | import android.animation.TimeInterpolator;
22 | import android.animation.ValueAnimator;
23 | import android.content.Context;
24 | import android.content.res.Resources;
25 | import android.os.Build;
26 | import android.support.annotation.AttrRes;
27 | import android.support.annotation.NonNull;
28 | import android.support.annotation.Nullable;
29 | import android.support.annotation.RequiresApi;
30 | import android.support.annotation.StyleRes;
31 | import android.support.v4.view.animation.FastOutSlowInInterpolator;
32 | import android.util.AttributeSet;
33 | import android.util.TypedValue;
34 | import android.view.View;
35 | import android.widget.FrameLayout;
36 | import android.widget.RelativeLayout;
37 | import android.widget.TextView;
38 |
39 | import jp.s64.android.navigationbarview.R;
40 | import jp.s64.android.navigationbarview.item.INavigationBarItem;
41 | import jp.s64.android.navigationbarview.view.INavigationBarItemView;
42 | import jp.s64.android.radiobuttonextended.core.widget.RadioFrameLayout;
43 |
44 | public class BottomNavigationBarItemView extends RadioFrameLayout implements INavigationBarItemView {
45 |
46 | private RelativeLayout mContainer;
47 | private FrameLayout mImageWrapper;
48 | private FrameLayout mTextWrapper;
49 | private TextView mText;
50 |
51 | private INavigationBarItem mItem;
52 |
53 | private Integer inactiveWidth = null;
54 | private Integer activeWidth = null;
55 | private Integer height = null;
56 |
57 | private Float mActiveTextScale, mInactiveTextScale, mDisableTextScale;
58 |
59 | private TimeInterpolator mInterpolator;
60 | private Long mDuration;
61 |
62 | private Integer mTextUnderPaddingVisible, mTextUnderPaddingGone;
63 |
64 | private Integer mTextSize;
65 |
66 | private boolean mOldIsChecked = false;
67 |
68 | public BottomNavigationBarItemView(@NonNull Context context) {
69 | super(context);
70 | init();
71 | }
72 |
73 | public BottomNavigationBarItemView(@NonNull Context context, @Nullable AttributeSet attrs) {
74 | super(context, attrs);
75 | init();
76 | }
77 |
78 | public BottomNavigationBarItemView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
79 | super(context, attrs, defStyleAttr);
80 | init();
81 | }
82 |
83 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
84 | public BottomNavigationBarItemView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
85 | super(context, attrs, defStyleAttr, defStyleRes);
86 | init();
87 | }
88 |
89 | protected void init() {
90 | {
91 | mActiveTextScale = getFloat(getResources(), R.dimen.active_item_with_text_text_scale);
92 | mInactiveTextScale = getFloat(getResources(), R.dimen.inactive_item_with_text_text_scale);
93 | mDisableTextScale = getFloat(getResources(), R.dimen.item_without_text_text_scale);
94 | }
95 | {
96 | setInterpolator(new FastOutSlowInInterpolator());
97 | setDuration(115l);
98 | }
99 | {
100 | mTextUnderPaddingVisible = getResources().getDimensionPixelSize(R.dimen.item_with_text_under_padding);
101 | mTextUnderPaddingGone = getResources().getDimensionPixelSize(R.dimen.item_without_text_under_padding);
102 | }
103 | {
104 | mTextSize = getResources().getDimensionPixelSize(R.dimen.item_text_size);
105 | }
106 | inflate(getContext(), R.layout.view_bottom_navigation_bar_item, this);
107 | {
108 | mContainer = (RelativeLayout) findViewById(R.id.root);
109 | mImageWrapper = (FrameLayout) findViewById(R.id.icon_wrapper);
110 | mTextWrapper = (FrameLayout) findViewById(R.id.text_wrapper);
111 | mText = (TextView) findViewById(R.id.text);
112 | }
113 | resetLayout();
114 | }
115 |
116 | @Override
117 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
118 | if (inactiveWidth != null && activeWidth != null && height != null) {
119 | super.onMeasure(
120 | MeasureSpec.makeMeasureSpec(getMeasuringWidth(), MeasureSpec.EXACTLY),
121 | MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
122 | );
123 | } else {
124 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
125 | }
126 | }
127 |
128 | protected Integer getMeasuringWidth() {
129 | return isChecked() ? activeWidth : inactiveWidth;
130 | }
131 |
132 | @Override
133 | public void setWidth(int inactive, int active) {
134 | {
135 | this.inactiveWidth = inactive;
136 | this.activeWidth = active;
137 | }
138 | requestLayout();
139 | }
140 |
141 | @Override
142 | public void setHeight(int height) {
143 | this.height = height;
144 | requestLayout();
145 | }
146 |
147 | @Override
148 | public void setChecked(boolean isChecked) {
149 | super.setChecked(isChecked);
150 | resetLayout();
151 | requestLayout();
152 | }
153 |
154 | @Override
155 | public void setItem(INavigationBarItem item) {
156 | {
157 | mItem = item;
158 | }
159 | setId(mItem.getIdRes());
160 | resetLayout();
161 | }
162 |
163 | protected void resetLayout() {
164 | if (mItem == null || mContainer == null || mText == null) {
165 | return;
166 | }
167 | final boolean oldWithTextState;
168 | final float oldScale;
169 | {
170 | oldWithTextState = mText.getAlpha() != 0f;
171 | oldScale = mText.getScaleX();
172 | }
173 | final boolean isChecked = isChecked();
174 | boolean withText;
175 | {
176 | String text = mItem.getText(isChecked);
177 | if (text != null) {
178 | mText.setText(text);
179 | withText = true;
180 | } else {
181 | withText = false;
182 | }
183 | }
184 | float textScale;
185 | {
186 | textScale = withText ? (isChecked ? mActiveTextScale : mInactiveTextScale) : mDisableTextScale;
187 | }
188 | int textSize = withText ? (int) (mTextSize * textScale) : 0;
189 | {
190 | int padding = withText ? mTextUnderPaddingVisible : mTextUnderPaddingGone;
191 | mImageWrapper.setPadding(
192 | mImageWrapper.getPaddingLeft(),
193 | mImageWrapper.getPaddingTop(),
194 | mImageWrapper.getPaddingRight(),
195 | padding + textSize
196 | );
197 | }
198 | {
199 | mTextWrapper.setPadding(
200 | mTextWrapper.getPaddingLeft(),
201 | mTextWrapper.getPaddingTop(),
202 | mTextWrapper.getPaddingRight(),
203 | mTextUnderPaddingVisible
204 | );
205 | }
206 | final ValueAnimator fraction, aTextAlpha, aTextScale;
207 | {
208 | aTextAlpha = ValueAnimator.ofFloat(
209 | oldWithTextState ? 1f : 0f,
210 | withText ? 1f : 0f
211 | );
212 | aTextAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
213 | @Override
214 | public void onAnimationUpdate(ValueAnimator animation) {
215 | mText.setAlpha((Float) animation.getAnimatedValue());
216 | }
217 | });
218 | }
219 | {
220 | aTextScale = ValueAnimator.ofFloat(
221 | oldScale,
222 | textScale
223 | );
224 | aTextScale.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
225 | @Override
226 | public void onAnimationUpdate(ValueAnimator animation) {
227 | mText.setScaleX((Float) animation.getAnimatedValue());
228 | mText.setScaleY((Float) animation.getAnimatedValue());
229 | }
230 | });
231 | }
232 | final boolean oldIsChecked = Boolean.valueOf(mOldIsChecked);
233 | {
234 | fraction = ValueAnimator.ofFloat(0, 1);
235 | fraction.setDuration(mDuration);
236 | fraction.setInterpolator(mInterpolator);
237 | fraction.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
238 | @Override
239 | public void onAnimationUpdate(ValueAnimator animation) {
240 | {
241 | View original = null, newIcon;
242 | for (int i = 0; i < mImageWrapper.getChildCount(); i++) {
243 | View view = mImageWrapper.getChildAt(i);
244 | if (original == null) {
245 | original = view;
246 | } else {
247 | mImageWrapper.removeView(view);
248 | }
249 | }
250 | Integer iconSize = mItem.getIconPixelSize();
251 | newIcon = mItem.updateIcon(
252 | original,
253 | getContext(),
254 | iconSize != null ? iconSize : getResources().getDimensionPixelSize(R.dimen.item_icon_size),
255 | iconSize != null ? iconSize : getResources().getDimensionPixelSize(R.dimen.item_icon_size),
256 | new INavigationBarItem.IconAnimator(isChecked, oldIsChecked, animation.getAnimatedFraction())
257 | );
258 | if (original != newIcon) {
259 | mImageWrapper.removeView(original);
260 | mImageWrapper.addView(newIcon);
261 | }
262 | }
263 | {
264 | ArgbEvaluator ev = new ArgbEvaluator();
265 | mText.setTextColor(
266 | (Integer) ev.evaluate(animation.getAnimatedFraction(), mItem.getTextColorInt(oldIsChecked), mItem.getTextColorInt(isChecked))
267 | );
268 | }
269 | }
270 | });
271 | fraction.addListener(new Animator.AnimatorListener() {
272 | @Override
273 | public void onAnimationStart(Animator animation) {
274 | {
275 | mOldIsChecked = Boolean.valueOf(isChecked);
276 | }
277 | {
278 | aTextAlpha.setDuration(fraction.getDuration());
279 | aTextScale.setDuration(fraction.getDuration());
280 | }
281 | {
282 | aTextAlpha.setInterpolator(fraction.getInterpolator());
283 | aTextScale.setInterpolator(fraction.getInterpolator());
284 | }
285 | {
286 | aTextAlpha.setInterpolator(fraction.getInterpolator());
287 | aTextScale.setInterpolator(fraction.getInterpolator());
288 | }
289 | {
290 | aTextAlpha.start();
291 | aTextScale.start();
292 | }
293 | }
294 |
295 | @Override
296 | public void onAnimationEnd(Animator animation) {
297 |
298 | }
299 |
300 | @Override
301 | public void onAnimationCancel(Animator animation) {
302 | {
303 | aTextAlpha.cancel();
304 | aTextScale.cancel();
305 | }
306 | }
307 |
308 | @Override
309 | public void onAnimationRepeat(Animator animation) {
310 |
311 | }
312 | });
313 | }
314 | fraction.start();
315 | }
316 |
317 | protected static float getFloat(Resources res, int resId) {
318 | TypedValue out = new TypedValue();
319 | res.getValue(resId, out, true);
320 | return out.getFloat();
321 | }
322 |
323 | @Override
324 | public void setDuration(long duration) {
325 | mDuration = duration;
326 | }
327 |
328 | @Override
329 | public void setInterpolator(TimeInterpolator interpolator) {
330 | mInterpolator = interpolator;
331 | }
332 |
333 | }
334 |
--------------------------------------------------------------------------------
/navigationbarview/src/main/java/jp/s64/android/navigationbarview/bottom/BottomNavigationBarView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Shuma Yoshioka
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jp.s64.android.navigationbarview.bottom;
18 |
19 | import android.animation.Animator;
20 | import android.animation.TimeInterpolator;
21 | import android.animation.ValueAnimator;
22 | import android.content.Context;
23 | import android.content.res.TypedArray;
24 | import android.os.Build;
25 | import android.support.annotation.AttrRes;
26 | import android.support.annotation.IdRes;
27 | import android.support.annotation.NonNull;
28 | import android.support.annotation.Nullable;
29 | import android.support.annotation.RequiresApi;
30 | import android.support.annotation.StyleRes;
31 | import android.support.design.widget.CoordinatorLayout;
32 | import android.support.transition.AutoTransition;
33 | import android.support.transition.TransitionManager;
34 | import android.support.transition.TransitionSet;
35 | import android.support.v4.view.ViewCompat;
36 | import android.support.v4.view.animation.FastOutSlowInInterpolator;
37 | import android.util.AttributeSet;
38 | import android.view.Gravity;
39 | import android.view.View;
40 | import android.widget.FrameLayout;
41 | import android.widget.RadioGroup;
42 |
43 | import com.google.common.collect.ImmutableList;
44 |
45 | import jp.s64.android.navigationbarview.R;
46 | import jp.s64.android.navigationbarview.item.INavigationBarItem;
47 | import jp.s64.android.navigationbarview.view.INavigationBarView;
48 | import jp.s64.android.navigationbarview.view.NavigationBarViewHelper;
49 | import jp.s64.android.radiobuttonextended.core.widget.CompoundFrameLayoutRadioGroup;
50 |
51 | public class BottomNavigationBarView extends FrameLayout implements INavigationBarView, NavigationBarViewHelper.IListener {
52 |
53 | private final NavigationBarViewHelper mHelper = new NavigationBarViewHelper<>(this);
54 |
55 | private TransitionSet mTransitionSet;
56 |
57 | private int mInactiveMinWidth;
58 | private int mInactiveMaxWidth;
59 | private int mActiveMaxWidth;
60 | private int mItemHeight;
61 |
62 | private boolean mItemWidthFixed = false;
63 |
64 | private CompoundFrameLayoutRadioGroup mItemsContainer;
65 | private CompoundFrameLayoutRadioGroup.OnCheckedChangeListener mCheckChanged;
66 |
67 | @Nullable
68 | private ValueAnimator mShowAnimator = null;
69 |
70 | @Nullable
71 | private ValueAnimator mHideAnimator = null;
72 |
73 | private OnLayoutChangeListener mInitialLayoutListener;
74 |
75 | public BottomNavigationBarView(@NonNull Context context) {
76 | super(context, null);
77 | init(null, 0, 0);
78 | }
79 |
80 | public BottomNavigationBarView(@NonNull Context context, @Nullable AttributeSet attrs) {
81 | super(context, attrs, 0);
82 | init(attrs, 0, 0);
83 | }
84 |
85 | public BottomNavigationBarView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
86 | super(context, attrs, defStyleAttr);
87 | init(attrs, defStyleAttr, 0);
88 | }
89 |
90 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
91 | public BottomNavigationBarView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
92 | super(context, attrs, defStyleAttr, defStyleRes);
93 | init(attrs, defStyleAttr, defStyleRes);
94 | }
95 |
96 | protected void init(@Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
97 | {
98 | mInitialLayoutListener = new OnLayoutChangeListener() {
99 | @Override
100 | public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
101 | removeOnLayoutChangeListener(mInitialLayoutListener);
102 | add(); // empty adding
103 | }
104 | };
105 | addOnLayoutChangeListener(mInitialLayoutListener);
106 | }
107 | {
108 | mInactiveMinWidth = getResources().getDimensionPixelSize(R.dimen.item_min_width);
109 | mInactiveMaxWidth = getResources().getDimensionPixelSize(R.dimen.inactive_item_max_width);
110 | mActiveMaxWidth = getResources().getDimensionPixelSize(R.dimen.active_item_max_width);
111 | mItemHeight = getResources().getDimensionPixelSize(R.dimen.horizontal_height);
112 | }
113 | {
114 | mTransitionSet = new AutoTransition();
115 | mTransitionSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
116 | setDuration(200l);
117 | setInterpolator(new FastOutSlowInInterpolator());
118 |
119 | }
120 | {
121 | mCheckChanged = new CompoundFrameLayoutRadioGroup.OnCheckedChangeListener() {
122 |
123 | @Override
124 | public void onCheckedChanged(CompoundFrameLayoutRadioGroup group, @IdRes int checkedId) {
125 | TransitionManager.beginDelayedTransition(mItemsContainer, mTransitionSet);
126 | {
127 | mHelper.onCheckChanged(checkedId);
128 | }
129 | }
130 |
131 | };
132 | }
133 | {
134 | mItemsContainer = new CompoundFrameLayoutRadioGroup(getContext());
135 | mItemsContainer.setOnCheckedChangeListener(mCheckChanged);
136 | addView(mItemsContainer);
137 | }
138 | {
139 | mItemsContainer.setGravity(Gravity.CENTER_HORIZONTAL);
140 | mItemsContainer.clearCheck();
141 | }
142 | float defaultElevation = getResources().getDimension(R.dimen.horizontal_elevation);
143 | {
144 | TypedArray a = null;
145 | try {
146 | a = getContext().obtainStyledAttributes(attrs, R.styleable.BottomNavigationBarView, defStyleAttr, defStyleRes);
147 | {
148 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
149 | float elevation = a.getDimension(R.styleable.BottomNavigationBarView_android_elevation, defaultElevation);
150 | ViewCompat.setElevation(this, elevation);
151 | }
152 | }
153 | {
154 | int orientation = a.getInteger(R.styleable.BottomNavigationBarView_android_orientation, RadioGroup.HORIZONTAL);
155 | mItemsContainer.setOrientation(orientation);
156 | }
157 | } finally {
158 | a.recycle();
159 | }
160 | }
161 | }
162 |
163 | @Override
164 | public void clearItems() {
165 | mHelper.clearItems();
166 | }
167 |
168 | @Override
169 | public void add(INavigationBarItem... items) {
170 | mHelper.add(items);
171 | }
172 |
173 | @Override
174 | public void remove(INavigationBarItem... items) {
175 | mHelper.remove(items);
176 | }
177 |
178 | @Override
179 | public void remove(int index) {
180 | mHelper.remove(index);
181 | }
182 |
183 | @Override
184 | public void replace(int index, INavigationBarItem item) {
185 | mHelper.replace(index, item);
186 | }
187 |
188 | @Override
189 | public int size() {
190 | return mHelper.size();
191 | }
192 |
193 | @Override
194 | public void setItemLimit(@Nullable Integer min, @Nullable Integer max) {
195 | mHelper.setItemLimit(min, max);
196 | }
197 |
198 | @Override
199 | public int getMaxSize() {
200 | return mHelper.getMaxSize();
201 | }
202 |
203 | @Override
204 | public int getMinSize() {
205 | return mHelper.getMinSize();
206 | }
207 |
208 | @Override
209 | public void check(@IdRes int idRes) {
210 | mItemsContainer.check(idRes);
211 | }
212 |
213 | @Override
214 | public void uncheck() {
215 | mItemsContainer.clearCheck();
216 | }
217 |
218 | @Nullable
219 | @Override
220 | public View getItemView(@IdRes int idRes) {
221 | for (int i = 0; i < mItemsContainer.getChildCount(); i++) {
222 | View view = mItemsContainer.getChildAt(i);
223 | if (view.getId() == idRes) {
224 | return view;
225 | }
226 | }
227 | return null;
228 | }
229 |
230 | @Override
231 | public void setOnCheckChangedListener(@Nullable OnCheckChangeListener listener) {
232 | mHelper.setOnCheckChangedListener(listener);
233 | }
234 |
235 | @Override
236 | public void show(final OnVisibilityAnimateListener doAnimate) {
237 | if (mShowAnimator != null) {
238 | return;
239 | }
240 | if (mHideAnimator != null) {
241 | mHideAnimator.cancel();
242 | mHideAnimator = null;
243 | }
244 |
245 | if (doAnimate != null) {
246 | final ValueAnimator innerAnimator = ValueAnimator.ofFloat(mItemsContainer.getAlpha(), 1);
247 | innerAnimator.setDuration(500l);
248 | innerAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
249 | @Override
250 | public void onAnimationUpdate(ValueAnimator animation) {
251 | mItemsContainer.setAlpha((Float) animation.getAnimatedValue());
252 | }
253 | });
254 | innerAnimator.setInterpolator(new FastOutSlowInInterpolator());
255 |
256 | mShowAnimator = ValueAnimator.ofFloat(getTranslationY(), 0);
257 | mShowAnimator.setDuration(300l);
258 | mShowAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
259 | @Override
260 | public void onAnimationUpdate(ValueAnimator animation) {
261 | setTranslationY((Float) animation.getAnimatedValue());
262 | doAnimate.onVisibilityAnimate(BottomNavigationBarView.this);
263 | }
264 | });
265 | mShowAnimator.addListener(new Animator.AnimatorListener() {
266 | @Override
267 | public void onAnimationStart(Animator animation) {
268 | innerAnimator.start();
269 | }
270 |
271 | @Override
272 | public void onAnimationEnd(Animator animation) {
273 |
274 | }
275 |
276 | @Override
277 | public void onAnimationCancel(Animator animation) {
278 | innerAnimator.cancel();
279 | }
280 |
281 | @Override
282 | public void onAnimationRepeat(Animator animation) {
283 |
284 | }
285 | });
286 | mShowAnimator.setInterpolator(new FastOutSlowInInterpolator());
287 | mShowAnimator.start();
288 | }
289 | }
290 |
291 | @Override
292 | public void hide(final OnVisibilityAnimateListener doAnimate) {
293 | if (mHideAnimator != null) {
294 | return;
295 | }
296 | if (mShowAnimator != null) {
297 | mShowAnimator.cancel();
298 | mShowAnimator = null;
299 | }
300 |
301 | if (doAnimate != null) {
302 | final ValueAnimator innerAnimator = ValueAnimator.ofFloat(mItemsContainer.getAlpha(), 0);
303 | innerAnimator.setDuration(250l);
304 | innerAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
305 | @Override
306 | public void onAnimationUpdate(ValueAnimator animation) {
307 | mItemsContainer.setAlpha((Float) animation.getAnimatedValue());
308 | }
309 | });
310 | innerAnimator.setInterpolator(new FastOutSlowInInterpolator());
311 |
312 | mHideAnimator = ValueAnimator.ofFloat(getTranslationY(), getHeight());
313 | mHideAnimator.setDuration(450l);
314 | mHideAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
315 | @Override
316 | public void onAnimationUpdate(ValueAnimator animation) {
317 | setTranslationY((Float) animation.getAnimatedValue());
318 | doAnimate.onVisibilityAnimate(BottomNavigationBarView.this);
319 | }
320 | });
321 | mHideAnimator.addListener(new Animator.AnimatorListener() {
322 | @Override
323 | public void onAnimationStart(Animator animation) {
324 | innerAnimator.start();
325 | }
326 |
327 | @Override
328 | public void onAnimationEnd(Animator animation) {
329 |
330 | }
331 |
332 | @Override
333 | public void onAnimationCancel(Animator animation) {
334 | innerAnimator.cancel();
335 | }
336 |
337 | @Override
338 | public void onAnimationRepeat(Animator animation) {
339 |
340 | }
341 | });
342 | mHideAnimator.setInterpolator(new FastOutSlowInInterpolator());
343 | mHideAnimator.start();
344 | }
345 | }
346 |
347 | @Override
348 | public boolean isNavigationBarShown() {
349 | return getTranslationY() == 0;
350 | }
351 |
352 | @IdRes
353 | @Override
354 | public int getChecked() {
355 | return mItemsContainer.getCheckedRadioButtonId();
356 | }
357 |
358 | @Override
359 | public void onItemsChanged(ImmutableList oldItems, ImmutableList newItems) {
360 | {
361 | mHelper.onItemsChanged(mItemsContainer, oldItems, newItems);
362 | }
363 | int measuredWidth = mItemsContainer.getMeasuredWidth();
364 | int count = mItemsContainer.getChildCount();
365 | int inactiveCount;
366 | {
367 | int activeCount = mItemsContainer.getCheckedRadioButtonId() != View.NO_ID ? 1 : 0;
368 | inactiveCount = count - activeCount;
369 | }
370 | int activeWidth, inactiveWidth;
371 | if (count == 0) {
372 | inactiveWidth = activeWidth = 0;
373 | } else if (mItemWidthFixed || count == 1) {
374 | inactiveWidth = activeWidth = Math.min(mActiveMaxWidth, measuredWidth / count);
375 | } else {
376 | {
377 | int layoutMax = measuredWidth - (inactiveCount * mInactiveMinWidth);
378 | activeWidth = Math.min(layoutMax, mActiveMaxWidth);
379 | }
380 | {
381 | int layoutMax = (measuredWidth - activeWidth) / inactiveCount;
382 | inactiveWidth = Math.min(layoutMax, mInactiveMaxWidth);
383 | }
384 | }
385 | for (int i = 0; i < count; i++) {
386 | BottomNavigationBarItemView itm = (BottomNavigationBarItemView) mItemsContainer.getChildAt(i);
387 | itm.setWidth(inactiveWidth, activeWidth);
388 | itm.setHeight(mItemHeight);
389 | itm.setDuration(mTransitionSet.getDuration());
390 | itm.setInterpolator(mTransitionSet.getInterpolator());
391 | }
392 | }
393 |
394 | @Override
395 | public BottomNavigationBarItemView createItemView(INavigationBarItem item, boolean isChecked) {
396 | BottomNavigationBarItemView ret = new BottomNavigationBarItemView(getContext());
397 | FrameLayout.LayoutParams params;
398 | {
399 | params = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
400 | }
401 | {
402 | ret.setChecked(isChecked);
403 | ret.setLayoutParams(params);
404 | ret.setItem(item);
405 | }
406 | return ret;
407 | }
408 |
409 | @Override
410 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
411 | final int h;
412 | if (size() < 1) {
413 | if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
414 | h = heightMeasureSpec;
415 | } else {
416 | h = MeasureSpec.makeMeasureSpec(mItemHeight, MeasureSpec.EXACTLY);
417 | }
418 | } else {
419 | h = heightMeasureSpec;
420 | }
421 | super.onMeasure(widthMeasureSpec, h);
422 | }
423 |
424 | public void setItemWidthFixed(boolean fixed) {
425 | mItemWidthFixed = fixed;
426 | mHelper.add(); // empty adding
427 | }
428 |
429 | @Override
430 | public void setDuration(long duration) {
431 | mTransitionSet.setDuration(duration);
432 | }
433 |
434 | @Override
435 | public void setInterpolator(TimeInterpolator interpolator) {
436 | mTransitionSet.setInterpolator(interpolator);
437 | }
438 |
439 | public static class BottomBehavior extends CoordinatorLayout.Behavior {
440 |
441 | public BottomBehavior() {
442 | }
443 |
444 | public BottomBehavior(Context context, AttributeSet attrs) {
445 | super(context, attrs);
446 | }
447 |
448 | @Override
449 | public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, BottomNavigationBarView child, View target, int dx, int dy, int[] consumed) {
450 | super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
451 | boolean isScrollDown = dy > 0;
452 |
453 | if (isScrollDown) {
454 | child.hide(createAnimator(target));
455 | } else {
456 | child.show(createAnimator(target));
457 | }
458 | }
459 |
460 | @Override
461 | public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, BottomNavigationBarView child, View directTargetChild, View target, int nestedScrollAxes) {
462 | return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
463 | }
464 |
465 | protected OnVisibilityAnimateListener createAnimator(View target) {
466 | return new OnVisibilityAnimateListener() {
467 | @Override
468 | public void onVisibilityAnimate(SELF self) {
469 | // no-op
470 | }
471 | };
472 | }
473 |
474 | }
475 |
476 | public static class LiftUpBottomBehavior extends BottomBehavior {
477 |
478 | public LiftUpBottomBehavior() {
479 | super();
480 | }
481 |
482 | public LiftUpBottomBehavior(Context context, AttributeSet attrs) {
483 | super(context, attrs);
484 | }
485 |
486 | @Override
487 | protected OnVisibilityAnimateListener createAnimator(final View target) {
488 | return new OnVisibilityAnimateListener() {
489 |
490 | @Override
491 | public void onVisibilityAnimate(SELF self) {
492 | MarginLayoutParams params = (MarginLayoutParams) target.getLayoutParams();
493 | params.bottomMargin = (int) (self.getTranslationY() - self.getHeight());
494 | target.setLayoutParams(params);
495 | }
496 |
497 | };
498 | }
499 |
500 | }
501 |
502 | }
503 |
--------------------------------------------------------------------------------