package tellh.com.gitclub.presentation.widget; import android.animation.Animator; import android.content.Context; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.support.annotation.ColorInt; import android.support.annotation.ColorRes; import android.support.design.widget.CoordinatorLayout; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.animation.LinearOutSlowInInterpolator; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.view.animation.OvershootInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.OnNavigationPositionListener; import com.aurelhubert.ahbottomnavigation.AHBottomNavigationBehavior; import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem; import com.aurelhubert.ahbottomnavigation.AHHelper; import java.util.ArrayList; import java.util.List; import tellh.com.gitclub.R; public class AHBottomNavigation extends FrameLayout { // Constant public static final int CURRENT_ITEM_NONE = -1; public static final int UPDATE_ALL_NOTIFICATIONS = -1; // Static private static String TAG = "AHBottomNavigation"; private static final int MIN_ITEMS = 3; private static final int MAX_ITEMS = 5; // Listener private OnTabSelectedListener tabSelectedListener; private OnNavigationPositionListener navigationPositionListener; // Variables private Context context; private Resources resources; private ArrayList<AHBottomNavigationItem> items = new ArrayList<>(); private ArrayList<View> views = new ArrayList<>(); private AHBottomNavigationBehavior<AHBottomNavigation> bottomNavigationBehavior; private View backgroundColorView; private Animator circleRevealAnim; private boolean colored = false; private String[] notifications = {"", "", "", "", ""}; private boolean isBehaviorTranslationSet = false; private int currentItem = 0; private int currentColor = 0; private boolean behaviorTranslationEnabled = true; private boolean needHideBottomNavigation = false; private boolean hideBottomNavigationWithAnimation = false; // Variables (Styles) private Typeface titleTypeface; private int defaultBackgroundColor = Color.WHITE; private @ColorInt int itemActiveColor; private @ColorInt int itemInactiveColor; private @ColorInt int titleColorActive; private @ColorInt int titleColorInactive; private @ColorInt int coloredTitleColorActive; private @ColorInt int coloredTitleColorInactive; private float titleActiveTextSize, titleInactiveTextSize; private int bottomNavigationHeight; private float selectedItemWidth, notSelectedItemWidth; private boolean forceTint = false; private boolean forceTitlesDisplay = false; // Notifications private @ColorInt int notificationTextColor; private @ColorInt int notificationBackgroundColor; private Drawable notificationBackgroundDrawable; private Typeface notificationTypeface; private int notificationActiveMarginLeft, notificationInactiveMarginLeft; private int notificationActiveMarginTop, notificationInactiveMarginTop; /** * Constructors */ public AHBottomNavigation(Context context) { super(context); init(context); } public AHBottomNavigation(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public AHBottomNavigation(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); createItems(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (!isBehaviorTranslationSet) { //The translation behavior has to be set up after the super.onMeasure has been called. isBehaviorTranslationSet = true; } } @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable("superState", super.onSaveInstanceState()); bundle.putInt("current_item", currentItem); bundle.putStringArray("notifications", notifications); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; currentItem = bundle.getInt("current_item"); notifications = bundle.getStringArray("notifications"); state = bundle.getParcelable("superState"); } super.onRestoreInstanceState(state); } ///////////// // PRIVATE // ///////////// /** * Init * * @param context */ private void init(Context context) { this.context = context; resources = this.context.getResources(); notificationTextColor = ContextCompat.getColor(context, android.R.color.white); bottomNavigationHeight = (int) resources.getDimension(R.dimen.bottom_navigation_height); // Item colors titleColorActive = ContextCompat.getColor(context, R.color.colorPrimary); titleColorInactive = ContextCompat.getColor(context, R.color.gray); // Colors for colored bottom navigation coloredTitleColorActive = ContextCompat.getColor(context, R.color.colorBottomNavigationActiveColored); coloredTitleColorInactive = ContextCompat.getColor(context, R.color.colorBottomNavigationInactiveColored); itemActiveColor = titleColorActive; itemInactiveColor = titleColorInactive; // Notifications notificationActiveMarginLeft = (int) resources.getDimension(R.dimen.bottom_navigation_notification_margin_left_active); notificationInactiveMarginLeft = (int) resources.getDimension(R.dimen.bottom_navigation_notification_margin_left); notificationActiveMarginTop = (int) resources.getDimension(R.dimen.bottom_navigation_notification_margin_top_active); notificationInactiveMarginTop = (int) resources.getDimension(R.dimen.bottom_navigation_notification_margin_top); ViewCompat.setElevation(this, resources.getDimension(R.dimen.bottom_navigation_elevation)); setClipToPadding(false); ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, bottomNavigationHeight); setLayoutParams(params); } /** * Create the items in the bottom navigation */ private void createItems() { if (items.size() < MIN_ITEMS) { Log.w(TAG, "The items list should have at least 3 items"); } else if (items.size() > MAX_ITEMS) { Log.w(TAG, "The items list should not have more than 5 items"); } int layoutHeight = (int) resources.getDimension(R.dimen.bottom_navigation_height); removeAllViews(); views.clear(); backgroundColorView = new View(context); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { LayoutParams backgroundLayoutParams = new LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, layoutHeight); addView(backgroundColorView, backgroundLayoutParams); } LinearLayout linearLayout = new LinearLayout(context); linearLayout.setOrientation(LinearLayout.HORIZONTAL); linearLayout.setGravity(Gravity.CENTER); LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, layoutHeight); addView(linearLayout, layoutParams); if (items.size() == MIN_ITEMS || forceTitlesDisplay) { createClassicItems(linearLayout); } else { createSmallItems(linearLayout); } // Force a request layout after all the items have been created post(new Runnable() { @Override public void run() { requestLayout(); } }); } /** * Create classic items (only 3 items in the bottom navigation) * * @param linearLayout The layout where the items are added */ private void createClassicItems(LinearLayout linearLayout) { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); float height = resources.getDimension(R.dimen.bottom_navigation_height); float minWidth = resources.getDimension(R.dimen.bottom_navigation_min_width); float maxWidth = resources.getDimension(R.dimen.bottom_navigation_max_width); if (forceTitlesDisplay && items.size() > MIN_ITEMS) { minWidth = resources.getDimension(R.dimen.bottom_navigation_small_inactive_min_width); maxWidth = resources.getDimension(R.dimen.bottom_navigation_small_inactive_max_width); } int layoutWidth = getWidth(); if (layoutWidth == 0 || items.size() == 0) { return; } float itemWidth = layoutWidth / items.size(); if (itemWidth < minWidth) { itemWidth = minWidth; } else if (itemWidth > maxWidth) { itemWidth = maxWidth; } float activeSize = resources.getDimension(R.dimen.bottom_navigation_text_size_active); float inactiveSize = resources.getDimension(R.dimen.bottom_navigation_text_size_inactive); int activePaddingTop = (int) resources.getDimension(R.dimen.bottom_navigation_margin_top_active); if (titleActiveTextSize != 0 && titleInactiveTextSize != 0) { activeSize = titleActiveTextSize; inactiveSize = titleInactiveTextSize; } else if (forceTitlesDisplay && items.size() > MIN_ITEMS) { activeSize = resources.getDimension(R.dimen.bottom_navigation_text_size_forced_active); inactiveSize = resources.getDimension(R.dimen.bottom_navigation_text_size_forced_inactive); } for (int i = 0; i < items.size(); i++) { final int itemIndex = i; AHBottomNavigationItem item = items.get(itemIndex); View view = inflater.inflate(R.layout.bottom_navigation_item, this, false); FrameLayout container = (FrameLayout) view.findViewById(R.id.bottom_navigation_container); ImageView icon = (ImageView) view.findViewById(R.id.bottom_navigation_item_icon); TextView title = (TextView) view.findViewById(R.id.bottom_navigation_item_title); TextView notification = (TextView) view.findViewById(R.id.bottom_navigation_notification); icon.setImageDrawable(item.getDrawable(context)); title.setText(item.getTitle(context)); if (titleTypeface != null) { title.setTypeface(titleTypeface); } if (forceTitlesDisplay && items.size() > MIN_ITEMS) { container.setPadding(0, container.getPaddingTop(), 0, container.getPaddingBottom()); } if (i == currentItem) { icon.setSelected(true); // Update margins (icon & notification) if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) icon.getLayoutParams(); p.setMargins(p.leftMargin, activePaddingTop, p.rightMargin, p.bottomMargin); ViewGroup.MarginLayoutParams paramsNotification = (ViewGroup.MarginLayoutParams) notification.getLayoutParams(); paramsNotification.setMargins(notificationActiveMarginLeft, paramsNotification.topMargin, paramsNotification.rightMargin, paramsNotification.bottomMargin); view.requestLayout(); } } else { icon.setSelected(false); ViewGroup.MarginLayoutParams paramsNotification = (ViewGroup.MarginLayoutParams) notification.getLayoutParams(); paramsNotification.setMargins(notificationInactiveMarginLeft, paramsNotification.topMargin, paramsNotification.rightMargin, paramsNotification.bottomMargin); } if (colored) { if (i == currentItem) { setBackgroundColor(item.getColor(context)); currentColor = item.getColor(context); } } else { setBackgroundColor(defaultBackgroundColor); } icon.setImageDrawable(AHHelper.getTintDrawable(items.get(i).getDrawable(context), currentItem == i ? itemActiveColor : itemInactiveColor, forceTint)); title.setTextColor(currentItem == i ? itemActiveColor : itemInactiveColor); title.setTextSize(TypedValue.COMPLEX_UNIT_PX, currentItem == i ? activeSize : inactiveSize); view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { updateItems(itemIndex, true); } }); LayoutParams params = new LayoutParams((int) itemWidth, (int) height); linearLayout.addView(view, params); views.add(view); } updateNotifications(true, UPDATE_ALL_NOTIFICATIONS); } /** * Create small items (more than 3 items in the bottom navigation) * * @param linearLayout The layout where the items are added */ private void createSmallItems(LinearLayout linearLayout) { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); float height = resources.getDimension(R.dimen.bottom_navigation_height); float minWidth = resources.getDimension(R.dimen.bottom_navigation_small_inactive_min_width); float maxWidth = resources.getDimension(R.dimen.bottom_navigation_small_inactive_max_width); int layoutWidth = getWidth(); if (layoutWidth == 0 || items.size() == 0) { return; } float itemWidth = layoutWidth / items.size(); if (itemWidth < minWidth) { itemWidth = minWidth; } else if (itemWidth > maxWidth) { itemWidth = maxWidth; } int activeMarginTop = (int) resources.getDimension(R.dimen.bottom_navigation_small_margin_top_active); float difference = resources.getDimension(R.dimen.bottom_navigation_small_selected_width_difference); selectedItemWidth = itemWidth + items.size() * difference; itemWidth -= difference; notSelectedItemWidth = itemWidth; for (int i = 0; i < items.size(); i++) { final int itemIndex = i; AHBottomNavigationItem item = items.get(itemIndex); View view = inflater.inflate(R.layout.bottom_navigation_small_item, this, false); ImageView icon = (ImageView) view.findViewById(R.id.bottom_navigation_small_item_icon); TextView title = (TextView) view.findViewById(R.id.bottom_navigation_small_item_title); TextView notification = (TextView) view.findViewById(R.id.bottom_navigation_notification); icon.setImageDrawable(item.getDrawable(context)); title.setText(item.getTitle(context)); if (titleActiveTextSize != 0) { title.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleActiveTextSize); } if (titleTypeface != null) { title.setTypeface(titleTypeface); } if (i == currentItem) { icon.setSelected(true); // Update margins (icon & notification) if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) icon.getLayoutParams(); p.setMargins(p.leftMargin, activeMarginTop, p.rightMargin, p.bottomMargin); ViewGroup.MarginLayoutParams paramsNotification = (ViewGroup.MarginLayoutParams) notification.getLayoutParams(); paramsNotification.setMargins(notificationActiveMarginLeft, notificationActiveMarginTop, paramsNotification.rightMargin, paramsNotification.bottomMargin); view.requestLayout(); } } else { icon.setSelected(false); ViewGroup.MarginLayoutParams paramsNotification = (ViewGroup.MarginLayoutParams) notification.getLayoutParams(); paramsNotification.setMargins(notificationInactiveMarginLeft, notificationInactiveMarginTop, paramsNotification.rightMargin, paramsNotification.bottomMargin); } if (colored) { if (i == currentItem) { setBackgroundColor(item.getColor(context)); currentColor = item.getColor(context); } } else { setBackgroundColor(defaultBackgroundColor); } icon.setImageDrawable(AHHelper.getTintDrawable(items.get(i).getDrawable(context), currentItem == i ? itemActiveColor : itemInactiveColor, forceTint)); title.setTextColor(currentItem == i ? itemActiveColor : itemInactiveColor); title.setAlpha(currentItem == i ? 1 : 0); view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { updateSmallItems(itemIndex, true); } }); LayoutParams params = new LayoutParams(i == currentItem ? (int) selectedItemWidth : (int) itemWidth, (int) height); linearLayout.addView(view, params); views.add(view); } updateNotifications(true, UPDATE_ALL_NOTIFICATIONS); } /** * Update Items UI * * @param itemIndex int: Selected item position * @param useCallback boolean: Use or not the callback */ private void updateItems(final int itemIndex, boolean useCallback) { if (currentItem == itemIndex) { if (tabSelectedListener != null && useCallback) { tabSelectedListener.onTabSelected(itemIndex, true); } return; } if (tabSelectedListener != null && useCallback) { boolean selectionAllowed = tabSelectedListener.onTabSelected(itemIndex, false); if (!selectionAllowed) return; } int activeMarginTop = (int) resources.getDimension(R.dimen.bottom_navigation_margin_top_active); int inactiveMarginTop = (int) resources.getDimension(R.dimen.bottom_navigation_margin_top_inactive); float activeSize = resources.getDimension(R.dimen.bottom_navigation_text_size_active); float inactiveSize = resources.getDimension(R.dimen.bottom_navigation_text_size_inactive); if (titleActiveTextSize != 0 && titleInactiveTextSize != 0) { activeSize = titleActiveTextSize; inactiveSize = titleInactiveTextSize; } else if (forceTitlesDisplay && items.size() > MIN_ITEMS) { activeSize = resources.getDimension(R.dimen.bottom_navigation_text_size_forced_active); inactiveSize = resources.getDimension(R.dimen.bottom_navigation_text_size_forced_inactive); } for (int i = 0; i < views.size(); i++) { if (i == itemIndex) { final TextView title = (TextView) views.get(itemIndex).findViewById(R.id.bottom_navigation_item_title); final ImageView icon = (ImageView) views.get(itemIndex).findViewById(R.id.bottom_navigation_item_icon); final TextView notification = (TextView) views.get(itemIndex).findViewById(R.id.bottom_navigation_notification); icon.setSelected(true); AHHelper.updateTopMargin(icon, inactiveMarginTop, activeMarginTop); AHHelper.updateLeftMargin(notification, notificationInactiveMarginLeft, notificationActiveMarginLeft); AHHelper.updateTextColor(title, itemInactiveColor, itemActiveColor); AHHelper.updateTextSize(title, inactiveSize, activeSize); AHHelper.updateDrawableColor(context, items.get(itemIndex).getDrawable(context), icon, itemInactiveColor, itemActiveColor, forceTint); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && colored) { int finalRadius = Math.max(getWidth(), getHeight()); int cx = (int) views.get(itemIndex).getX() + views.get(itemIndex).getWidth() / 2; int cy = views.get(itemIndex).getHeight() / 2; if (circleRevealAnim != null && circleRevealAnim.isRunning()) { circleRevealAnim.cancel(); setBackgroundColor(items.get(itemIndex).getColor(context)); backgroundColorView.setBackgroundColor(Color.TRANSPARENT); } circleRevealAnim = ViewAnimationUtils.createCircularReveal(backgroundColorView, cx, cy, 0, finalRadius); circleRevealAnim.setStartDelay(5); circleRevealAnim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { backgroundColorView.setBackgroundColor(items.get(itemIndex).getColor(context)); } @Override public void onAnimationEnd(Animator animation) { setBackgroundColor(items.get(itemIndex).getColor(context)); backgroundColorView.setBackgroundColor(Color.TRANSPARENT); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); circleRevealAnim.start(); } else if (colored) { AHHelper.updateViewBackgroundColor(this, currentColor, items.get(itemIndex).getColor(context)); } else { setBackgroundColor(defaultBackgroundColor); backgroundColorView.setBackgroundColor(Color.TRANSPARENT); } } else if (i == currentItem) { final TextView title = (TextView) views.get(currentItem).findViewById(R.id.bottom_navigation_item_title); final ImageView icon = (ImageView) views.get(currentItem).findViewById(R.id.bottom_navigation_item_icon); final TextView notification = (TextView) views.get(currentItem).findViewById(R.id.bottom_navigation_notification); icon.setSelected(false); AHHelper.updateTopMargin(icon, activeMarginTop, inactiveMarginTop); AHHelper.updateLeftMargin(notification, notificationActiveMarginLeft, notificationInactiveMarginLeft); AHHelper.updateTextColor(title, itemActiveColor, itemInactiveColor); AHHelper.updateTextSize(title, activeSize, inactiveSize); AHHelper.updateDrawableColor(context, items.get(currentItem).getDrawable(context), icon, itemActiveColor, itemInactiveColor, forceTint); } } currentItem = itemIndex; if (currentItem > 0 && currentItem < items.size()) { currentColor = items.get(currentItem).getColor(context); } else if (currentItem == CURRENT_ITEM_NONE) { setBackgroundColor(defaultBackgroundColor); backgroundColorView.setBackgroundColor(Color.TRANSPARENT); } /* if (tabSelectedListener != null && useCallback) { tabSelectedListener.onTabSelected(itemIndex, false); } */ } /** * Update Small items UI * * @param itemIndex int: Selected item position * @param useCallback boolean: Use or not the callback */ private void updateSmallItems(final int itemIndex, boolean useCallback) { if (currentItem == itemIndex) { if (tabSelectedListener != null && useCallback) { tabSelectedListener.onTabSelected(itemIndex, true); } return; } if (tabSelectedListener != null && useCallback) { boolean selectionAllowed = tabSelectedListener.onTabSelected(itemIndex, false); if (!selectionAllowed) return; } int activeMarginTop = (int) resources.getDimension(R.dimen.bottom_navigation_small_margin_top_active); int inactiveMargin = (int) resources.getDimension(R.dimen.bottom_navigation_small_margin_top); for (int i = 0; i < views.size(); i++) { if (i == itemIndex) { final FrameLayout container = (FrameLayout) views.get(itemIndex).findViewById(R.id.bottom_navigation_small_container); final TextView title = (TextView) views.get(itemIndex).findViewById(R.id.bottom_navigation_small_item_title); final ImageView icon = (ImageView) views.get(itemIndex).findViewById(R.id.bottom_navigation_small_item_icon); final TextView notification = (TextView) views.get(itemIndex).findViewById(R.id.bottom_navigation_notification); icon.setSelected(true); AHHelper.updateTopMargin(icon, inactiveMargin, activeMarginTop); AHHelper.updateLeftMargin(notification, notificationInactiveMarginLeft, notificationActiveMarginLeft); AHHelper.updateTopMargin(notification, notificationInactiveMarginTop, notificationActiveMarginTop); AHHelper.updateTextColor(title, itemInactiveColor, itemActiveColor); AHHelper.updateAlpha(title, 0, 1); AHHelper.updateWidth(container, notSelectedItemWidth, selectedItemWidth); AHHelper.updateDrawableColor(context, items.get(itemIndex).getDrawable(context), icon, itemInactiveColor, itemActiveColor, forceTint); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && colored) { int finalRadius = Math.max(getWidth(), getHeight()); int cx = (int) views.get(itemIndex).getX() + views.get(itemIndex).getWidth() / 2; int cy = views.get(itemIndex).getHeight() / 2; if (circleRevealAnim != null && circleRevealAnim.isRunning()) { circleRevealAnim.cancel(); setBackgroundColor(items.get(itemIndex).getColor(context)); backgroundColorView.setBackgroundColor(Color.TRANSPARENT); } circleRevealAnim = ViewAnimationUtils.createCircularReveal(backgroundColorView, cx, cy, 0, finalRadius); circleRevealAnim.setStartDelay(5); circleRevealAnim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { backgroundColorView.setBackgroundColor(items.get(itemIndex).getColor(context)); } @Override public void onAnimationEnd(Animator animation) { setBackgroundColor(items.get(itemIndex).getColor(context)); backgroundColorView.setBackgroundColor(Color.TRANSPARENT); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); circleRevealAnim.start(); } else if (colored) { AHHelper.updateViewBackgroundColor(this, currentColor, items.get(itemIndex).getColor(context)); } else { setBackgroundColor(defaultBackgroundColor); backgroundColorView.setBackgroundColor(Color.TRANSPARENT); } } else if (i == currentItem) { final View container = views.get(currentItem).findViewById(R.id.bottom_navigation_small_container); final TextView title = (TextView) views.get(currentItem).findViewById(R.id.bottom_navigation_small_item_title); final ImageView icon = (ImageView) views.get(currentItem).findViewById(R.id.bottom_navigation_small_item_icon); final TextView notification = (TextView) views.get(currentItem).findViewById(R.id.bottom_navigation_notification); icon.setSelected(false); AHHelper.updateTopMargin(icon, activeMarginTop, inactiveMargin); AHHelper.updateLeftMargin(notification, notificationActiveMarginLeft, notificationInactiveMarginLeft); AHHelper.updateTopMargin(notification, notificationActiveMarginTop, notificationInactiveMarginTop); AHHelper.updateTextColor(title, itemActiveColor, itemInactiveColor); AHHelper.updateAlpha(title, 1, 0); AHHelper.updateWidth(container, selectedItemWidth, notSelectedItemWidth); AHHelper.updateDrawableColor(context, items.get(currentItem).getDrawable(context), icon, itemActiveColor, itemInactiveColor, forceTint); } } currentItem = itemIndex; if (currentItem > 0 && currentItem < items.size()) { currentColor = items.get(currentItem).getColor(context); } else if (currentItem == CURRENT_ITEM_NONE) { setBackgroundColor(defaultBackgroundColor); backgroundColorView.setBackgroundColor(Color.TRANSPARENT); } /* if (tabSelectedListener != null && useCallback) { tabSelectedListener.onTabSelected(itemIndex, false); } */ } /** * Update notifications */ private void updateNotifications(boolean updateStyle, int itemPosition) { for (int i = 0; i < views.size(); i++) { if (itemPosition != UPDATE_ALL_NOTIFICATIONS && itemPosition != i) { continue; } TextView notification = (TextView) views.get(i).findViewById(R.id.bottom_navigation_notification); String currentValue = notification.getText().toString(); boolean animate = !currentValue.equals(String.valueOf(notifications[i])); if (updateStyle) { notification.setTextColor(notificationTextColor); if (notificationTypeface != null) { notification.setTypeface(notificationTypeface); } else { notification.setTypeface(null, Typeface.BOLD); } if (notificationBackgroundDrawable != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { Drawable drawable = notificationBackgroundDrawable.getConstantState().newDrawable(); notification.setBackground(drawable); } else { notification.setBackgroundDrawable(notificationBackgroundDrawable); } } else if (notificationBackgroundColor != 0) { Drawable defautlDrawable = ContextCompat.getDrawable(context, R.drawable.notification_background); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { notification.setBackground(AHHelper.getTintDrawable(defautlDrawable, notificationBackgroundColor, forceTint)); } else { notification.setBackgroundDrawable(AHHelper.getTintDrawable(defautlDrawable, notificationBackgroundColor, forceTint)); } } } if (notifications[i].length() == 0 && notification.getText().length() > 0) { notification.setText(""); if (animate) { notification.animate() .scaleX(0) .scaleY(0) .alpha(0) .setInterpolator(new AccelerateInterpolator()) .setDuration(150) .start(); } } else if (notifications[i].length() > 0) { notification.setText(String.valueOf(notifications[i])); if (animate) { notification.setScaleX(0); notification.setScaleY(0); notification.animate() .scaleX(1) .scaleY(1) .alpha(1) .setInterpolator(new OvershootInterpolator()) .setDuration(150) .start(); } } } } //////////// // PUBLIC // //////////// /** * Add an item */ public void addItem(AHBottomNavigationItem item) { if (this.items.size() > MAX_ITEMS) { Log.w(TAG, "The items list should not have more than 5 items"); } items.add(item); createItems(); } /** * Add all items */ public void addItems(List<AHBottomNavigationItem> items) { if (items.size() > MAX_ITEMS || (this.items.size() + items.size()) > MAX_ITEMS) { Log.w(TAG, "The items list should not have more than 5 items"); } this.items.addAll(items); createItems(); } /** * Remove an item at the given index */ public void removeItemAtIndex(int index) { if (index < items.size()) { this.items.remove(index); createItems(); } } /** * Remove all items */ public void removeAllItems() { this.items.clear(); createItems(); } /** * Refresh the AHBottomView */ public void refresh() { createItems(); } /** * Return the number of items * * @return int */ public int getItemsCount() { return items.size(); } /** * Return if the Bottom Navigation is colored */ public boolean isColored() { return colored; } /** * Set if the Bottom Navigation is colored */ public void setColored(boolean colored) { this.colored = colored; this.itemActiveColor = colored ? coloredTitleColorActive : titleColorActive; this.itemInactiveColor = colored ? coloredTitleColorInactive : titleColorInactive; createItems(); } /** * Return the bottom navigation background color * * @return The bottom navigation background color */ public int getDefaultBackgroundColor() { return defaultBackgroundColor; } /** * Set the bottom navigation background color * * @param defaultBackgroundColor The bottom navigation background color */ public void setDefaultBackgroundColor(@ColorInt int defaultBackgroundColor) { this.defaultBackgroundColor = defaultBackgroundColor; createItems(); } /** * Get the accent color (used when the view contains 3 items) * * @return The default accent color */ public int getAccentColor() { return itemActiveColor; } /** * Set the accent color (used when the view contains 3 items) * * @param accentColor The new accent color */ public void setAccentColor(int accentColor) { this.titleColorActive = accentColor; this.itemActiveColor = accentColor; createItems(); } /** * Get the inactive color (used when the view contains 3 items) * * @return The inactive color */ public int getInactiveColor() { return itemInactiveColor; } /** * Set the inactive color (used when the view contains 3 items) * * @param inactiveColor The inactive color */ public void setInactiveColor(int inactiveColor) { this.titleColorInactive = inactiveColor; this.itemInactiveColor = inactiveColor; createItems(); } /** * Set the colors used when the bottom bar uses the colored mode * * @param colorActive The active color * @param colorInactive The inactive color */ public void setColoredModeColors(@ColorInt int colorActive, @ColorInt int colorInactive) { this.coloredTitleColorActive = colorActive; this.coloredTitleColorInactive = colorInactive; createItems(); } /** * Set notification typeface * * @param typeface Typeface */ public void setTitleTypeface(Typeface typeface) { this.titleTypeface = typeface; createItems(); } /** * Set title text size * * @param activeSize * @param inactiveSize */ public void setTitleTextSize(float activeSize, float inactiveSize) { this.titleActiveTextSize = activeSize; this.titleInactiveTextSize = inactiveSize; createItems(); } /** * Get item at the given index * * @param position int: item position * @return The item at the given position */ public AHBottomNavigationItem getItem(int position) { if (position < 0 || position > items.size() - 1) { Log.w(TAG, "The position is out of bounds of the items (" + items.size() + " elements)"); } return items.get(position); } /** * Get the current item * * @return The current item position */ public int getCurrentItem() { return currentItem; } /** * Set the current item * * @param position int: position */ public void setCurrentItem(int position) { setCurrentItem(position, true); } /** * Set the current item * * @param position int: item position * @param useCallback boolean: use or not the callback */ public void setCurrentItem(int position, boolean useCallback) { if (position >= items.size()) { Log.w(TAG, "The position is out of bounds of the items (" + items.size() + " elements)"); return; } if (items.size() == MIN_ITEMS || forceTitlesDisplay) { updateItems(position, useCallback); } else { updateSmallItems(position, useCallback); } } /** * Return if the behavior translation is enabled * * @return a boolean value */ public boolean isBehaviorTranslationEnabled() { return behaviorTranslationEnabled; } /** * Hide Bottom Navigation with animation */ public void hideBottomNavigation() { hideBottomNavigation(true); } /** * Hide Bottom Navigation with or without animation * * @param withAnimation Boolean */ public void hideBottomNavigation(boolean withAnimation) { if (bottomNavigationBehavior != null) { bottomNavigationBehavior.hideView(this, bottomNavigationHeight, withAnimation); } else if (getParent() instanceof CoordinatorLayout) { needHideBottomNavigation = true; hideBottomNavigationWithAnimation = withAnimation; } else { // Hide bottom navigation ViewCompat.animate(this) .translationY(bottomNavigationHeight) .setInterpolator(new LinearOutSlowInInterpolator()) .setDuration(withAnimation ? 300 : 0) .start(); } } /** * Restore Bottom Navigation with animation */ public void restoreBottomNavigation() { restoreBottomNavigation(true); } /** * Restore Bottom Navigation with or without animation * * @param withAnimation Boolean */ public void restoreBottomNavigation(boolean withAnimation) { if (bottomNavigationBehavior != null) { bottomNavigationBehavior.resetOffset(this, withAnimation); } else { // Show bottom navigation ViewCompat.animate(this) .translationY(0) .setInterpolator(new LinearOutSlowInInterpolator()) .setDuration(withAnimation ? 300 : 0) .start(); } } /** * Return if the tint should be forced (with setColorFilter) * * @return Boolean */ public boolean isForceTint() { return forceTint; } /** * Set the force tint value * If forceTint = true, the tint is made with drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN); * * @param forceTint Boolean */ public void setForceTint(boolean forceTint) { this.forceTint = forceTint; createItems(); } /** * Return if we force the titles to be displayed * * @return Boolean */ public boolean isForceTitlesDisplay() { return forceTitlesDisplay; } /** * Force the titles to be displayed (or used the classic behavior) * Note: Against Material Design guidelines * * @param forceTitlesDisplay Boolean */ public void setForceTitlesDisplay(boolean forceTitlesDisplay) { this.forceTitlesDisplay = forceTitlesDisplay; createItems(); } /** * Set AHOnTabSelectedListener */ public void setOnTabSelectedListener(OnTabSelectedListener tabSelectedListener) { this.tabSelectedListener = tabSelectedListener; } /** * Remove AHOnTabSelectedListener */ public void removeOnTabSelectedListener() { this.tabSelectedListener = null; } /** * Set OnNavigationPositionListener */ public void setOnNavigationPositionListener(OnNavigationPositionListener navigationPositionListener) { this.navigationPositionListener = navigationPositionListener; if (bottomNavigationBehavior != null) { bottomNavigationBehavior.setOnNavigationPositionListener(navigationPositionListener); } } /** * Remove OnNavigationPositionListener() */ public void removeOnNavigationPositionListener() { this.navigationPositionListener = null; if (bottomNavigationBehavior != null) { bottomNavigationBehavior.removeOnNavigationPositionListener(); } } /** * Set the notification number * * @param nbNotification int * @param itemPosition int */ @Deprecated public void setNotification(int nbNotification, int itemPosition) { if (itemPosition < 0 || itemPosition > items.size() - 1) { Log.w(TAG, "The position is out of bounds of the items (" + items.size() + " elements)"); return; } notifications[itemPosition] = nbNotification == 0 ? "" : String.valueOf(nbNotification); updateNotifications(false, itemPosition); } /** * Set Notification content * * @param title String * @param itemPosition int */ public void setNotification(String title, int itemPosition) { notifications[itemPosition] = title; updateNotifications(false, itemPosition); } /** * Set notification text color * * @param textColor int */ public void setNotificationTextColor(@ColorInt int textColor) { this.notificationTextColor = textColor; updateNotifications(true, UPDATE_ALL_NOTIFICATIONS); } /** * Set notification text color * * @param textColor int */ public void setNotificationTextColorResource(@ColorRes int textColor) { this.notificationTextColor = ContextCompat.getColor(context, textColor); updateNotifications(true, UPDATE_ALL_NOTIFICATIONS); } /** * Set notification background resource * * @param drawable Drawable */ public void setNotificationBackground(Drawable drawable) { this.notificationBackgroundDrawable = drawable; updateNotifications(true, UPDATE_ALL_NOTIFICATIONS); } /** * Set notification background color * * @param color int */ public void setNotificationBackgroundColor(@ColorInt int color) { this.notificationBackgroundColor = color; updateNotifications(true, UPDATE_ALL_NOTIFICATIONS); } /** * Set notification background color * * @param color int */ public void setNotificationBackgroundColorResource(@ColorRes int color) { this.notificationBackgroundColor = ContextCompat.getColor(context, color); updateNotifications(true, UPDATE_ALL_NOTIFICATIONS); } /** * Set notification typeface * * @param typeface Typeface */ public void setNotificationTypeface(Typeface typeface) { this.notificationTypeface = typeface; updateNotifications(true, UPDATE_ALL_NOTIFICATIONS); } /** * Set the notification margin left * * @param activeMargin * @param inactiveMargin */ public void setNotificationMarginLeft(int activeMargin, int inactiveMargin) { this.notificationActiveMarginLeft = activeMargin; this.notificationInactiveMarginLeft = inactiveMargin; createItems(); } /** * Activate or not the elevation * * @param useElevation boolean */ public void setUseElevation(boolean useElevation) { ViewCompat.setElevation(this, useElevation ? resources.getDimension(R.dimen.bottom_navigation_elevation) : 0); setClipToPadding(false); } /** * Activate or not the elevation, and set the value * * @param useElevation boolean * @param elevation float */ public void setUseElevation(boolean useElevation, float elevation) { ViewCompat.setElevation(this, useElevation ? elevation : 0); setClipToPadding(false); } //////////////// // INTERFACES // //////////////// /** * */ public interface OnTabSelectedListener { /** * Called when a tab has been selected (clicked) * * @param position int: Position of the selected tab * @param wasSelected boolean: true if the tab was already selected * @return boolean: true for updating the tab UI, false otherwise */ boolean onTabSelected(int position, boolean wasSelected); } }