2121import android .content .Context ;
2222import android .content .res .ColorStateList ;
2323import android .graphics .drawable .Drawable ;
24+ import android .graphics .drawable .GradientDrawable ;
25+ import android .graphics .drawable .RippleDrawable ;
2426import android .os .Build ;
27+ import android .os .Build .VERSION ;
28+ import android .os .Build .VERSION_CODES ;
2529import android .os .Bundle ;
2630import android .os .Parcel ;
2731import android .os .Parcelable ;
3539import com .google .android .material .behavior .HideBottomViewOnScrollBehavior ;
3640import com .google .android .material .internal .ThemeEnforcement ;
3741import com .google .android .material .resources .MaterialResources ;
42+ import com .google .android .material .ripple .RippleUtils ;
3843import com .google .android .material .shape .MaterialShapeDrawable ;
3944import androidx .coordinatorlayout .widget .CoordinatorLayout ;
4045import androidx .core .content .ContextCompat ;
@@ -104,6 +109,7 @@ public class BottomNavigationView extends FrameLayout {
104109 private final MenuBuilder menu ;
105110 private final BottomNavigationMenuView menuView ;
106111 private final BottomNavigationPresenter presenter = new BottomNavigationPresenter ();
112+ private ColorStateList itemRippleColor ;
107113 private MenuInflater menuInflater ;
108114
109115 private OnNavigationItemSelectedListener selectedListener ;
@@ -193,7 +199,14 @@ public BottomNavigationView(Context context, AttributeSet attrs, int defStyleAtt
193199 a .getBoolean (R .styleable .BottomNavigationView_itemHorizontalTranslationEnabled , true ));
194200
195201 int itemBackground = a .getResourceId (R .styleable .BottomNavigationView_itemBackground , 0 );
196- menuView .setItemBackgroundRes (itemBackground );
202+ if (itemBackground != 0 ) {
203+ menuView .setItemBackgroundRes (itemBackground );
204+ } else {
205+ ColorStateList itemRippleColor =
206+ MaterialResources .getColorStateList (
207+ context , a , R .styleable .BottomNavigationView_itemRippleColor );
208+ setItemRippleColor (itemRippleColor );
209+ }
197210
198211 if (a .hasValue (R .styleable .BottomNavigationView_menu )) {
199212 inflateMenu (a .getResourceId (R .styleable .BottomNavigationView_menu , 0 ));
@@ -367,11 +380,14 @@ public int getItemBackgroundResource() {
367380 /**
368381 * Set the background of our menu items to the given resource.
369382 *
383+ * <p>This will remove any ripple backgrounds created by {@link setItemRippleColor()}.
384+ *
370385 * @param resId The identifier of the resource.
371386 * @attr ref R.styleable#BottomNavigationView_itemBackground
372387 */
373388 public void setItemBackgroundResource (@ DrawableRes int resId ) {
374389 menuView .setItemBackgroundRes (resId );
390+ itemRippleColor = null ;
375391 }
376392
377393 /**
@@ -388,11 +404,63 @@ public Drawable getItemBackground() {
388404 /**
389405 * Set the background of our menu items to the given drawable.
390406 *
407+ * <p>This will remove any ripple backgrounds created by {@link setItemRippleColor()}.
408+ *
391409 * @param background The drawable for the background.
392410 * @attr ref R.styleable#BottomNavigationView_itemBackground
393411 */
394412 public void setItemBackground (@ Nullable Drawable background ) {
395413 menuView .setItemBackground (background );
414+ itemRippleColor = null ;
415+ }
416+
417+ /**
418+ * Returns the color used to create a ripple as the background drawable of the menu items. If a
419+ * background is set using {@link #setItemBackground()}, this will return null.
420+ *
421+ * @see #setItemBackground(Drawable)
422+ * @attr ref R.styleable#BottomNavigationView_itemRippleColor
423+ */
424+ public ColorStateList getItemRippleColor () {
425+ return itemRippleColor ;
426+ }
427+
428+ /**
429+ * Set the background of our menu items to be a ripple with the given colors.
430+ *
431+ * @param itemRippleColor The {@link ColorStateList} for the ripple. This will create a ripple
432+ * background for menu items, replacing any background previously set by {@link
433+ * #setItemBackground()}.
434+ * @attr ref R.styleable#BottomNavigationView_itemRippleColor
435+ */
436+ public void setItemRippleColor (ColorStateList itemRippleColor ) {
437+ if (this .itemRippleColor == itemRippleColor ) {
438+ // Clear the item background when setItemRippleColor(null) is called for consistency.
439+ if (itemRippleColor == null && menuView .getItemBackground () != null ) {
440+ menuView .setItemBackground (null );
441+ }
442+ return ;
443+ }
444+
445+ this .itemRippleColor = itemRippleColor ;
446+ if (itemRippleColor == null ) {
447+ menuView .setItemBackground (null );
448+ } else {
449+ ColorStateList rippleDrawableColor =
450+ RippleUtils .convertToRippleDrawableColor (itemRippleColor );
451+ if (VERSION .SDK_INT >= VERSION_CODES .LOLLIPOP ) {
452+ menuView .setItemBackground (new RippleDrawable (rippleDrawableColor , null , null ));
453+ } else {
454+ GradientDrawable rippleDrawable = new GradientDrawable ();
455+ // TODO: Find a workaround for this. Currently on certain devices/versions, LayerDrawable
456+ // will draw a black background underneath any layer with a non-opaque color,
457+ // (e.g. ripple) unless we set the shape to be something that's not a perfect rectangle.
458+ rippleDrawable .setCornerRadius (0.00001F );
459+ Drawable rippleDrawableCompat = DrawableCompat .wrap (rippleDrawable );
460+ DrawableCompat .setTintList (rippleDrawableCompat , rippleDrawableColor );
461+ menuView .setItemBackground (rippleDrawableCompat );
462+ }
463+ }
396464 }
397465
398466 /**
0 commit comments