@@ -98,13 +98,21 @@ public abstract class NavigationBarItemView extends FrameLayout implements MenuV
9898 @ Nullable private Drawable originalIconDrawable ;
9999 @ Nullable private Drawable wrappedIconDrawable ;
100100
101+ private static final ActiveIndicatorTransform ACTIVE_INDICATOR_LABELED_TRANSFORM =
102+ new ActiveIndicatorTransform ();
103+ private static final ActiveIndicatorTransform ACTIVE_INDICATOR_UNLABELED_TRANSFORM =
104+ new ActiveIndicatorUnlabeledTransform ();
105+
101106 private ValueAnimator activeIndicatorAnimator ;
107+ private ActiveIndicatorTransform activeIndicatorTransform = ACTIVE_INDICATOR_LABELED_TRANSFORM ;
102108 private float activeIndicatorProgress = 0F ;
103109 private boolean activeIndicatorEnabled = false ;
104110 // The desired width of the indicator. This is not necessarily the actual size of the rendered
105111 // indicator depending on whether the width of this view is wide enough to accommodate the full
106112 // desired width.
107113 private int activeIndicatorDesiredWidth = 0 ;
114+ private int activeIndicatorDesiredHeight = 0 ;
115+ private boolean activeIndicatorResizeable = false ;
108116 // The margin from the start and end of this view which the active indicator should respect. If
109117 // the indicator width is greater than the total width minus the horizontal margins, the active
110118 // indicator will assume the max width of the view's total width minus horizontal margins.
@@ -246,6 +254,8 @@ public void setShifting(boolean shifting) {
246254 public void setLabelVisibilityMode (@ NavigationBarView .LabelVisibility int mode ) {
247255 if (labelVisibilityMode != mode ) {
248256 labelVisibilityMode = mode ;
257+ updateActiveIndicatorTransform ();
258+ updateActiveIndicatorLayoutParams (getWidth ());
249259 refreshChecked ();
250260 }
251261 }
@@ -291,11 +301,19 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
291301 new Runnable () {
292302 @ Override
293303 public void run () {
294- updateActiveIndicatorAvailableWidth (width );
304+ updateActiveIndicatorLayoutParams (width );
295305 }
296306 });
297307 }
298308
309+ private void updateActiveIndicatorTransform () {
310+ if (isActiveIndicatorResizeableAndUnlabeled ()) {
311+ activeIndicatorTransform = ACTIVE_INDICATOR_UNLABELED_TRANSFORM ;
312+ } else {
313+ activeIndicatorTransform = ACTIVE_INDICATOR_LABELED_TRANSFORM ;
314+ }
315+ }
316+
299317 /**
300318 * Update the active indicator for a given 0-1 value.
301319 *
@@ -307,14 +325,8 @@ public void run() {
307325 */
308326 private void setActiveIndicatorProgress (
309327 @ FloatRange (from = 0F , to = 1F ) float progress , float target ) {
310- // TODO: Consider refactoring into a transform class.
311328 if (activeIndicatorView != null ) {
312- activeIndicatorView .setScaleX (AnimationUtils .lerp (.4f , 1f , progress ));
313- // Animate the alpha of the indicator over the first 1/5th of the animation
314- float startAlphaFraction = target == 0F ? .8F : 0F ;
315- float endAlphaFraction = target == 0F ? 1F : .2F ;
316- activeIndicatorView .setAlpha (
317- AnimationUtils .lerp (0f , 1f , startAlphaFraction , endAlphaFraction , progress ));
329+ activeIndicatorTransform .updateForProgress (progress , target , activeIndicatorView );
318330 }
319331 activeIndicatorProgress = progress ;
320332 }
@@ -681,15 +693,16 @@ public void setActiveIndicatorEnabled(boolean enabled) {
681693 */
682694 public void setActiveIndicatorWidth (int width ) {
683695 this .activeIndicatorDesiredWidth = width ;
684- updateActiveIndicatorAvailableWidth (getWidth ());
696+ updateActiveIndicatorLayoutParams (getWidth ());
685697 }
686698
687699 /**
688- * Update the active indicator to always be within the width of this item layout.
700+ * Update the active indicators width and height for the available width and label
701+ * visibility mode.
689702 *
690703 * @param availableWidth The total width of this item layout.
691704 */
692- private void updateActiveIndicatorAvailableWidth (int availableWidth ) {
705+ private void updateActiveIndicatorLayoutParams (int availableWidth ) {
693706 // Set width to the min of either the desired indicator width or the available width minus
694707 // a horizontal margin.
695708 if (activeIndicatorView == null ) {
@@ -698,38 +711,42 @@ private void updateActiveIndicatorAvailableWidth(int availableWidth) {
698711
699712 int newWidth =
700713 min (activeIndicatorDesiredWidth , availableWidth - (activeIndicatorMarginHorizontal * 2 ));
714+
701715 LayoutParams indicatorParams = (LayoutParams ) activeIndicatorView .getLayoutParams ();
716+ // If the label visibility is unlabeled, make the active indicator's height equal to it's width.
717+ indicatorParams .height =
718+ isActiveIndicatorResizeableAndUnlabeled () ? newWidth : activeIndicatorDesiredHeight ;
702719 indicatorParams .width = newWidth ;
703720 activeIndicatorView .setLayoutParams (indicatorParams );
704721 }
705722
723+ private boolean isActiveIndicatorResizeableAndUnlabeled () {
724+ return activeIndicatorResizeable
725+ && labelVisibilityMode == NavigationBarView .LABEL_VISIBILITY_UNLABELED ;
726+ }
727+
706728 /**
707729 * Set the desired height of the active indicator.
708730 *
709- * <p>TODO: Consider adjusting based on available height.
731+ * <p>TODO: Consider adjusting based on available height
710732 *
711733 * @param height The desired height of the active indicator.
712734 */
713735 public void setActiveIndicatorHeight (int height ) {
714- if (activeIndicatorView == null ) {
715- return ;
716- }
717-
718- LayoutParams indicatorParams = (LayoutParams ) activeIndicatorView .getLayoutParams ();
719- indicatorParams .height = height ;
720- activeIndicatorView .setLayoutParams (indicatorParams );
736+ activeIndicatorDesiredHeight = height ;
737+ updateActiveIndicatorLayoutParams (getWidth ());
721738 }
722739
723740 /**
724741 * Set the horizontal margin that will be maintained at the start and end of the active indicator,
725742 * making sure the indicator remains the given distance from the edge of this item view.
726743 *
727- * @see #updateActiveIndicatorAvailableWidth (int)
744+ * @see #updateActiveIndicatorLayoutParams (int)
728745 * @param marginHorizontal The horizontal margin, in pixels.
729746 */
730747 public void setActiveIndicatorMarginHorizontal (@ Px int marginHorizontal ) {
731748 this .activeIndicatorMarginHorizontal = marginHorizontal ;
732- updateActiveIndicatorAvailableWidth (getWidth ());
749+ updateActiveIndicatorLayoutParams (getWidth ());
733750 }
734751
735752 /** Get the drawable used as the active indicator. */
@@ -751,6 +768,11 @@ public void setActiveIndicatorDrawable(@Nullable Drawable activeIndicatorDrawabl
751768 activeIndicatorView .setBackgroundDrawable (activeIndicatorDrawable );
752769 }
753770
771+ /** Set whether the indicator can be automatically resized. */
772+ public void setActiveIndicatorResizeable (boolean resizeable ) {
773+ this .activeIndicatorResizeable = resizeable ;
774+ }
775+
754776 void setBadge (@ NonNull BadgeDrawable badgeDrawable ) {
755777 this .badgeDrawable = badgeDrawable ;
756778 if (icon != null ) {
@@ -869,4 +891,84 @@ protected int getItemDefaultMarginResId() {
869891 */
870892 @ LayoutRes
871893 protected abstract int getItemLayoutResId ();
894+
895+ /**
896+ * A class used to manipulate the {@link NavigationBarItemView}'s active indicator view when
897+ * animating between hidden and shown.
898+ *
899+ * <p>By default, this class scales the indicator in the x direction to reveal the default pill
900+ * shape.
901+ *
902+ * <p>Subclasses can override {@link #updateForProgress(float, float, View)} to manipulate the
903+ * view in any way appropriate.
904+ */
905+ private static class ActiveIndicatorTransform {
906+
907+ private static final float SCALE_X_HIDDEN = .4F ;
908+ private static final float SCALE_X_SHOWN = 1F ;
909+
910+ // The fraction of the animation's total duration over which the indicator will be faded in or
911+ // out.
912+ private static final float ALPHA_FRACTION = 1F / 5F ;
913+
914+ /**
915+ * Calculate the alpha value, based on a progress and target value, that has the indicator
916+ * appear or disappear over the first 1/5th of the transform.
917+ */
918+ protected float calculateAlpha (
919+ @ FloatRange (from = 0F , to = 1F ) float progress ,
920+ @ FloatRange (from = 0F , to = 1F ) float targetValue ) {
921+ // Animate the alpha of the indicator over the first ALPHA_FRACTION of the animation
922+ float startAlphaFraction = targetValue == 0F ? 1F - ALPHA_FRACTION : 0F ;
923+ float endAlphaFraction = targetValue == 0F ? 1F : 0F + ALPHA_FRACTION ;
924+ return AnimationUtils .lerp (0F , 1F , startAlphaFraction , endAlphaFraction , progress );
925+ }
926+
927+ protected float calculateScaleX (
928+ @ FloatRange (from = 0F , to = 1F ) float progress ,
929+ @ FloatRange (from = 0F , to = 1F ) float targetValue ) {
930+ return AnimationUtils .lerp (SCALE_X_HIDDEN , SCALE_X_SHOWN , progress );
931+ }
932+
933+ protected float calculateScaleY (
934+ @ FloatRange (from = 0F , to = 1F ) float progress ,
935+ @ FloatRange (from = 0F , to = 1F ) float targetValue ) {
936+ return 1F ;
937+ }
938+
939+ /**
940+ * Called whenever the {@code indicator} should update its parameters (scale, alpha, etc.) in
941+ * response to a change in progress.
942+ *
943+ * @param progress A value between 0 and 1 where 0 represents a fully hidden indicator and 1
944+ * indicates a fully shown indicator.
945+ * @param targetValue The final value towards which the progress is moving. This will be either
946+ * 0 and 1 and can be used to determine whether the indicator is showing or hiding if show
947+ * and hide animations differ.
948+ * @param indicator The active indicator {@link View}.
949+ */
950+ public void updateForProgress (
951+ @ FloatRange (from = 0F , to = 1F ) float progress ,
952+ @ FloatRange (from = 0F , to = 1F ) float targetValue ,
953+ @ NonNull View indicator ) {
954+ indicator .setScaleX (calculateScaleX (progress , targetValue ));
955+ indicator .setScaleY (calculateScaleY (progress , targetValue ));
956+ indicator .setAlpha (calculateAlpha (progress , targetValue ));
957+ }
958+ }
959+
960+ /**
961+ * A transform class used to animate the active indicator of a {@link NavigationBarItemView} that
962+ * is unlabeled.
963+ *
964+ * <p>This differs from the default {@link ActiveIndicatorTransform} class by uniformly scaling in
965+ * the X and Y axis.
966+ */
967+ private static class ActiveIndicatorUnlabeledTransform extends ActiveIndicatorTransform {
968+
969+ @ Override
970+ protected float calculateScaleY (float progress , float targetValue ) {
971+ return calculateScaleX (progress , targetValue );
972+ }
973+ }
872974}
0 commit comments