Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Ashok-Varma
authored and
Ashok-Varma
committed
Mar 19, 2016
0 parents
commit a93a5ca
Showing
78 changed files
with
1,384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
*.iml | ||
.gradle | ||
/local.properties | ||
/.idea/workspace.xml | ||
/.idea/libraries | ||
.DS_Store | ||
/build | ||
/captures |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
apply plugin: 'com.android.library' | ||
|
||
android { | ||
compileSdkVersion 23 | ||
buildToolsVersion "23.0.2" | ||
|
||
defaultConfig { | ||
minSdkVersion 14 | ||
targetSdkVersion 23 | ||
versionCode 1 | ||
versionName "1.0" | ||
} | ||
buildTypes { | ||
release { | ||
minifyEnabled false | ||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||
} | ||
} | ||
} | ||
|
||
dependencies { | ||
compile fileTree(dir: 'libs', include: ['*.jar']) | ||
testCompile 'junit:junit:4.12' | ||
compile 'com.android.support:appcompat-v7:23.2.1' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Add project specific ProGuard rules here. | ||
# By default, the flags in this file are appended to flags specified | ||
# in /Users/ashokvarma/Library/Android/sdk/tools/proguard/proguard-android.txt | ||
# You can edit the include path and order by changing the proguardFiles | ||
# directive in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# Add any project specific keep options here: | ||
|
||
# If your project uses WebView with JS, uncomment the following | ||
# and specify the fully qualified class name to the JavaScript interface | ||
# class: | ||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
# public *; | ||
#} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="com.ashokvarma.bottomnavigation"> | ||
|
||
<application android:allowBackup="true" | ||
android:label="@string/app_name" | ||
android:supportsRtl="true" | ||
> | ||
|
||
</application> | ||
|
||
</manifest> |
309 changes: 309 additions & 0 deletions
309
BottomNavigationBar/src/main/java/com/ashokvarma/bottomnavigation/BottomNavigationBar.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,309 @@ | ||
package com.ashokvarma.bottomnavigation; | ||
|
||
import android.annotation.TargetApi; | ||
import android.content.Context; | ||
import android.graphics.Color; | ||
import android.os.Build; | ||
import android.support.annotation.IntDef; | ||
import android.support.v4.view.ViewCompat; | ||
import android.util.AttributeSet; | ||
import android.util.Log; | ||
import android.view.LayoutInflater; | ||
import android.view.View; | ||
import android.view.ViewGroup; | ||
import android.widget.FrameLayout; | ||
import android.widget.LinearLayout; | ||
|
||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.util.ArrayList; | ||
|
||
/** | ||
* Class description | ||
* | ||
* @author ashokvarma | ||
* @version 1.0 | ||
* @see FrameLayout | ||
* @since 19 Mar 2016 | ||
*/ | ||
public class BottomNavigationBar extends FrameLayout { | ||
|
||
public static final int MODE_DEFAULT = 0; | ||
public static final int MODE_CLASSIC = 1; | ||
public static final int MODE_SHIFTING = 2; | ||
|
||
@IntDef({MODE_DEFAULT, MODE_CLASSIC, MODE_SHIFTING}) | ||
@Retention(RetentionPolicy.SOURCE) | ||
public @interface Mode { | ||
} | ||
|
||
public static final int BACKGROUND_STYLE_DEFAULT = 0; | ||
public static final int BACKGROUND_STYLE_STATIC = 1; | ||
public static final int BACKGROUND_STYLE_RIPPLE = 2; | ||
|
||
@IntDef({BACKGROUND_STYLE_DEFAULT, BACKGROUND_STYLE_STATIC, BACKGROUND_STYLE_RIPPLE}) | ||
@Retention(RetentionPolicy.SOURCE) | ||
public @interface BackgroundStyle { | ||
} | ||
|
||
@Mode | ||
private int mMode = MODE_DEFAULT; | ||
@BackgroundStyle | ||
private int mBackgroundStyle = BACKGROUND_STYLE_DEFAULT; | ||
|
||
private boolean mScrollable = false; | ||
|
||
private static final int MIN_SIZE = 3; | ||
private static final int MAX_SIZE = 5; | ||
|
||
ArrayList<BottomNavigationItem> bottomNavigationItems = new ArrayList<>(); | ||
ArrayList<BottomNavigationTab> bottomNavigationTabs = new ArrayList<>(); | ||
|
||
private int mSelectedPosition = -1; | ||
private OnTabSelectedListener mTabSelectedListener; | ||
|
||
private int mActiveColor; | ||
private int mInActiveColor; | ||
private int mBackgroundColor; | ||
|
||
private FrameLayout mBackgroundOverlay; | ||
private FrameLayout mContainer; | ||
private LinearLayout mTabContainer; | ||
|
||
private int mAnimationDuration = 200; | ||
private int mRippleAnimationDuration = 500; | ||
|
||
public BottomNavigationBar(Context context) { | ||
super(context); | ||
init(); | ||
} | ||
|
||
public BottomNavigationBar(Context context, AttributeSet attrs) { | ||
super(context, attrs); | ||
init(); | ||
} | ||
|
||
public BottomNavigationBar(Context context, AttributeSet attrs, int defStyleAttr) { | ||
super(context, attrs, defStyleAttr); | ||
init(); | ||
} | ||
|
||
@TargetApi(Build.VERSION_CODES.LOLLIPOP) | ||
public BottomNavigationBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | ||
super(context, attrs, defStyleAttr, defStyleRes); | ||
init(); | ||
} | ||
|
||
private void init() { | ||
|
||
setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) getContext().getResources().getDimension(R.dimen.bottom_navigation_height))); | ||
|
||
LayoutInflater inflater = LayoutInflater.from(getContext()); | ||
View parentView = inflater.inflate(R.layout.bottom_navigation_bar_container, this, true); | ||
mBackgroundOverlay = (FrameLayout) parentView.findViewById(R.id.bottom_navigation_bar_overLay); | ||
mContainer = (FrameLayout) parentView.findViewById(R.id.bottom_navigation_bar_container); | ||
mTabContainer = (LinearLayout) parentView.findViewById(R.id.bottom_navigation_bar_item_container); | ||
|
||
mActiveColor = Utils.fetchContextColor(getContext(), R.attr.colorAccent); | ||
mBackgroundColor = Color.WHITE; | ||
mInActiveColor = Color.LTGRAY; | ||
|
||
ViewCompat.setElevation(this, getContext().getResources().getDimension(R.dimen.bottom_navigation_elevation)); | ||
setClipToPadding(false); | ||
} | ||
|
||
@Override | ||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | ||
super.onMeasure(widthMeasureSpec, heightMeasureSpec); | ||
} | ||
|
||
public BottomNavigationBar addItem(BottomNavigationItem item) { | ||
bottomNavigationItems.add(item); | ||
return this; | ||
} | ||
|
||
public BottomNavigationBar removeItem(BottomNavigationItem item) { | ||
bottomNavigationItems.remove(item); | ||
return this; | ||
} | ||
|
||
public BottomNavigationBar setMode(@Mode int mode) { | ||
this.mMode = mode; | ||
return this; | ||
} | ||
|
||
public BottomNavigationBar setBackgroundStyle(@BackgroundStyle int backgroundStyle) { | ||
this.mBackgroundStyle = backgroundStyle; | ||
return this; | ||
} | ||
|
||
/** | ||
* will be public once all bugs are ressolved. | ||
*/ | ||
private BottomNavigationBar setScrollable(boolean scrollable) { | ||
mScrollable = scrollable; | ||
return this; | ||
} | ||
|
||
public void initialise() { | ||
if (bottomNavigationItems.size() > 0) { | ||
mTabContainer.removeAllViews(); | ||
if (mMode == MODE_DEFAULT) { | ||
if (bottomNavigationItems.size() <= MIN_SIZE) { | ||
mMode = MODE_CLASSIC; | ||
} else { | ||
mMode = MODE_SHIFTING; | ||
} | ||
} | ||
if (mBackgroundStyle == BACKGROUND_STYLE_DEFAULT) { | ||
if (mMode == MODE_CLASSIC) { | ||
mBackgroundStyle = BACKGROUND_STYLE_STATIC; | ||
} else { | ||
mBackgroundStyle = BACKGROUND_STYLE_RIPPLE; | ||
} | ||
} | ||
|
||
int screenWidth = Utils.getScreenWidth(getContext()); | ||
|
||
if (mMode == MODE_CLASSIC) { | ||
|
||
int widths[] = BottomNavigationUtils.getClassicMeasurements(getContext(), screenWidth, bottomNavigationItems.size(), mScrollable); | ||
int itemWidth = widths[0]; | ||
|
||
for (BottomNavigationItem currentItem : bottomNavigationItems) { | ||
ClassicBottomNavigationTab bottomNavigationTab = new ClassicBottomNavigationTab(getContext()); | ||
setUpTab(bottomNavigationTab, currentItem, itemWidth, itemWidth); | ||
} | ||
|
||
} else if (mMode == MODE_SHIFTING) { | ||
|
||
int widths[] = BottomNavigationUtils.getShiftingMeasurements(getContext(), screenWidth, bottomNavigationItems.size(), mScrollable); | ||
|
||
int itemWidth = widths[0]; | ||
int itemActiveWidth = widths[1]; | ||
|
||
for (BottomNavigationItem currentItem : bottomNavigationItems) { | ||
ShiftingBottomNavigationTab bottomNavigationTab = new ShiftingBottomNavigationTab(getContext()); | ||
setUpTab(bottomNavigationTab, currentItem, itemWidth, itemActiveWidth); | ||
} | ||
} | ||
|
||
if (bottomNavigationTabs.size() > 0) { | ||
selectTab(0, true); | ||
} | ||
} | ||
} | ||
|
||
private void setUpTab(BottomNavigationTab bottomNavigationTab, BottomNavigationItem currentItem, int itemWidth, int itemActiveWidth) { | ||
bottomNavigationTab.setInactiveWidth(itemWidth); | ||
bottomNavigationTab.setActiveWidth(itemActiveWidth); | ||
Log.e("widths", "inactive : " + itemWidth + ", active : " + itemActiveWidth); | ||
bottomNavigationTab.setPosition(bottomNavigationItems.indexOf(currentItem)); | ||
|
||
bottomNavigationTab.setOnClickListener(new OnClickListener() { | ||
@Override | ||
public void onClick(View v) { | ||
BottomNavigationTab bottomNavigationTabView = (BottomNavigationTab) v; | ||
selectTab(bottomNavigationTabView.getPosition(), false); | ||
} | ||
}); | ||
|
||
bottomNavigationTabs.add(bottomNavigationTab); | ||
|
||
BottomNavigationUtils.bindTabWithData(currentItem, bottomNavigationTab, this); | ||
|
||
bottomNavigationTab.initialise(); | ||
|
||
mTabContainer.addView(bottomNavigationTab); | ||
} | ||
|
||
private void selectTab(int newPosition, boolean firstTab) { | ||
if (mSelectedPosition != newPosition) { | ||
if (mBackgroundStyle == BACKGROUND_STYLE_STATIC) { | ||
if (mSelectedPosition != -1) | ||
bottomNavigationTabs.get(mSelectedPosition).unSelect(true, mAnimationDuration); | ||
bottomNavigationTabs.get(newPosition).select(true, mAnimationDuration); | ||
} else if (mBackgroundStyle == BACKGROUND_STYLE_RIPPLE) { | ||
if (mSelectedPosition != -1) | ||
bottomNavigationTabs.get(mSelectedPosition).unSelect(false, mAnimationDuration); | ||
bottomNavigationTabs.get(newPosition).select(false, mAnimationDuration); | ||
|
||
BottomNavigationTab clickedView = bottomNavigationTabs.get(newPosition); | ||
if (firstTab) { | ||
mContainer.setBackgroundColor(clickedView.getActiveColor()); | ||
} else { | ||
BottomNavigationUtils.setBackgroundWithRipple(clickedView, mContainer, mBackgroundOverlay, clickedView.getActiveColor(), mRippleAnimationDuration); | ||
} | ||
} | ||
mSelectedPosition = newPosition; | ||
} | ||
sendListenerCall(mSelectedPosition, newPosition); | ||
} | ||
|
||
private void sendListenerCall(int oldPosition, int newPosition) { | ||
if (mTabSelectedListener != null && oldPosition != -1) { | ||
if (oldPosition == newPosition) { | ||
mTabSelectedListener.onTabReselected(newPosition); | ||
} else { | ||
mTabSelectedListener.onTabSelected(newPosition); | ||
mTabSelectedListener.onTabUnselected(newPosition); | ||
} | ||
} | ||
} | ||
|
||
public void setTabSelectedListener(OnTabSelectedListener tabSelectedListener) { | ||
this.mTabSelectedListener = tabSelectedListener; | ||
} | ||
|
||
public void setActiveColor(int activeColor) { | ||
this.mActiveColor = activeColor; | ||
} | ||
|
||
public void setInActiveColor(int inActiveColor) { | ||
this.mInActiveColor = inActiveColor; | ||
} | ||
|
||
public void setBackgroundColor(int backgroundColor) { | ||
this.mBackgroundColor = backgroundColor; | ||
} | ||
|
||
public int getActiveColor() { | ||
return mActiveColor; | ||
} | ||
|
||
public int getInActiveColor() { | ||
return mInActiveColor; | ||
} | ||
|
||
public int getBackgroundColor() { | ||
return mBackgroundColor; | ||
} | ||
|
||
/** | ||
* Callback interface invoked when a tab's selection state changes. | ||
*/ | ||
public interface OnTabSelectedListener { | ||
|
||
/** | ||
* Called when a tab enters the selected state. | ||
* | ||
* @param position The position of the tab that was selected | ||
*/ | ||
public void onTabSelected(int position); | ||
|
||
/** | ||
* Called when a tab exits the selected state. | ||
* | ||
* @param position The position of the tab that was unselected | ||
*/ | ||
public void onTabUnselected(int position); | ||
|
||
/** | ||
* Called when a tab that is already selected is chosen again by the user. Some applications | ||
* may use this action to return to the top level of a category. | ||
* | ||
* @param position The position of the tab that was reselected. | ||
*/ | ||
public void onTabReselected(int position); | ||
} | ||
} |
Oops, something went wrong.