Skip to content

Commit d1895cd

Browse files
drchenpaulfthomas
authored andcommitted
[Tab] Mutate selected indicator drawable when it's being set
Wrapping and mutating a given drawable which is supposed to be tinted is a standard practice across the Material library. This CL applies the same logic to Tab's selected indicator drawables and also refactoring the relevant logic a bit to make it cleaner. Resolves #2794 PiperOrigin-RevId: 460765659
1 parent 201b4a0 commit d1895cd

File tree

2 files changed

+41
-32
lines changed

2 files changed

+41
-32
lines changed

lib/java/com/google/android/material/drawable/DrawableUtils.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@
3131
import android.text.TextUtils;
3232
import android.util.AttributeSet;
3333
import android.util.Xml;
34+
import androidx.annotation.ColorInt;
3435
import androidx.annotation.NonNull;
3536
import androidx.annotation.Nullable;
3637
import androidx.annotation.RestrictTo;
3738
import androidx.annotation.RestrictTo.Scope;
3839
import androidx.annotation.XmlRes;
40+
import androidx.core.graphics.drawable.DrawableCompat;
3941
import java.io.IOException;
4042
import java.lang.reflect.InvocationTargetException;
4143
import java.lang.reflect.Method;
@@ -52,6 +54,29 @@ public final class DrawableUtils {
5254

5355
private DrawableUtils() {}
5456

57+
/**
58+
* Tints the given {@link Drawable} with the given color. If the color is transparent, this
59+
* method will remove any set tints on the drawable.
60+
*/
61+
public static void setTint(@NonNull Drawable drawable, @ColorInt int color) {
62+
boolean hasTint = color != Color.TRANSPARENT;
63+
if (VERSION.SDK_INT == VERSION_CODES.LOLLIPOP) {
64+
// On API 21, AppCompat's WrappedDrawableApi21 class only supports tinting certain types of
65+
// drawables. Replicates the logic here to support all types of drawables.
66+
if (hasTint) {
67+
drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
68+
} else {
69+
drawable.setColorFilter(null);
70+
}
71+
} else {
72+
if (hasTint) {
73+
DrawableCompat.setTint(drawable, color);
74+
} else {
75+
DrawableCompat.setTintList(drawable, null);
76+
}
77+
}
78+
}
79+
5580
/** Returns a tint filter for the given tint and mode. */
5681
@Nullable
5782
public static PorterDuffColorFilter updateTintFilter(

lib/java/com/google/android/material/tabs/TabLayout.java

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import android.database.DataSetObserver;
3535
import android.graphics.Canvas;
3636
import android.graphics.Color;
37-
import android.graphics.PorterDuff;
3837
import android.graphics.Rect;
3938
import android.graphics.drawable.ColorDrawable;
4039
import android.graphics.drawable.Drawable;
@@ -92,6 +91,7 @@
9291
import androidx.viewpager.widget.ViewPager;
9392
import com.google.android.material.badge.BadgeDrawable;
9493
import com.google.android.material.badge.BadgeUtils;
94+
import com.google.android.material.drawable.DrawableUtils;
9595
import com.google.android.material.internal.ThemeEnforcement;
9696
import com.google.android.material.internal.ViewUtils;
9797
import com.google.android.material.resources.MaterialResources;
@@ -197,6 +197,8 @@ public class TabLayout extends HorizontalScrollView {
197197

198198
private static final int ANIMATION_DURATION = 300;
199199

200+
private static final int SELECTED_INDICATOR_HEIGHT_DEFAULT = -1;
201+
200202
private static final Pools.Pool<Tab> tabPool = new Pools.SynchronizedPool<>(16);
201203

202204
private static final String LOG_TAG = "TabLayout";
@@ -450,7 +452,7 @@ public interface BaseOnTabSelectedListener<T extends Tab> {
450452
ColorStateList tabTextColors;
451453
ColorStateList tabIconTint;
452454
ColorStateList tabRippleColorStateList;
453-
@NonNull Drawable tabSelectedIndicator = new GradientDrawable();
455+
@NonNull Drawable tabSelectedIndicator;
454456
private int tabSelectedIndicatorColor = Color.TRANSPARENT;
455457

456458
android.graphics.PorterDuff.Mode tabIconTintMode;
@@ -472,7 +474,7 @@ public interface BaseOnTabSelectedListener<T extends Tab> {
472474
@Mode int mode;
473475
boolean inlineLabel;
474476
boolean tabIndicatorFullWidth;
475-
int tabIndicatorHeight = -1;
477+
int tabIndicatorHeight = SELECTED_INDICATOR_HEIGHT_DEFAULT;
476478
@TabIndicatorAnimationMode int tabIndicatorAnimationMode;
477479
boolean unboundedRipple;
478480

@@ -640,6 +642,7 @@ public TabLayout(@NonNull Context context, @Nullable AttributeSet attrs, int def
640642
*/
641643
public void setSelectedTabIndicatorColor(@ColorInt int color) {
642644
this.tabSelectedIndicatorColor = color;
645+
DrawableUtils.setTint(tabSelectedIndicator, tabSelectedIndicatorColor);
643646
updateTabViews(false);
644647
}
645648

@@ -1377,15 +1380,16 @@ public Drawable getTabSelectedIndicator() {
13771380
* @see #setSelectedTabIndicator(int)
13781381
*/
13791382
public void setSelectedTabIndicator(@Nullable Drawable tabSelectedIndicator) {
1380-
if (this.tabSelectedIndicator != tabSelectedIndicator) {
1381-
this.tabSelectedIndicator =
1382-
tabSelectedIndicator != null ? tabSelectedIndicator : new GradientDrawable();
1383-
int indicatorHeight =
1384-
tabIndicatorHeight != -1
1385-
? tabIndicatorHeight
1386-
: this.tabSelectedIndicator.getIntrinsicHeight();
1387-
slidingTabIndicator.setSelectedIndicatorHeight(indicatorHeight);
1383+
if (tabSelectedIndicator == null) {
1384+
tabSelectedIndicator = new GradientDrawable();
13881385
}
1386+
this.tabSelectedIndicator = DrawableCompat.wrap(tabSelectedIndicator).mutate();
1387+
DrawableUtils.setTint(this.tabSelectedIndicator, tabSelectedIndicatorColor);
1388+
int indicatorHeight =
1389+
tabIndicatorHeight == SELECTED_INDICATOR_HEIGHT_DEFAULT
1390+
? this.tabSelectedIndicator.getIntrinsicHeight()
1391+
: tabIndicatorHeight;
1392+
slidingTabIndicator.setSelectedIndicatorHeight(indicatorHeight);
13891393
}
13901394

13911395
/**
@@ -3274,27 +3278,7 @@ public void draw(@NonNull Canvas canvas) {
32743278
Rect indicatorBounds = tabSelectedIndicator.getBounds();
32753279
tabSelectedIndicator.setBounds(
32763280
indicatorBounds.left, indicatorTop, indicatorBounds.right, indicatorBottom);
3277-
Drawable indicator = tabSelectedIndicator;
3278-
3279-
if (tabSelectedIndicatorColor != Color.TRANSPARENT) {
3280-
// If a tint color has been specified using TabLayout's setSelectedTabIndicatorColor, wrap
3281-
// the drawable and tint it as specified.
3282-
indicator = DrawableCompat.wrap(indicator);
3283-
if (VERSION.SDK_INT == VERSION_CODES.LOLLIPOP) {
3284-
indicator.setColorFilter(tabSelectedIndicatorColor, PorterDuff.Mode.SRC_IN);
3285-
} else {
3286-
DrawableCompat.setTint(indicator, tabSelectedIndicatorColor);
3287-
}
3288-
} else {
3289-
// Remove existing tint if setSelectedTabIndicatorColor to Color.Transparent.
3290-
if (VERSION.SDK_INT == VERSION_CODES.LOLLIPOP) {
3291-
indicator.setColorFilter(null);
3292-
} else {
3293-
DrawableCompat.setTintList(indicator, null);
3294-
}
3295-
}
3296-
3297-
indicator.draw(canvas);
3281+
tabSelectedIndicator.draw(canvas);
32983282
}
32993283

33003284
// Draw the tab item contents (icon and label) on top of the background + indicator layers

0 commit comments

Comments
 (0)