Skip to content

Commit

Permalink
[Color] Add ColorStateListDrawable support
Browse files Browse the repository at this point in the history
Resolves #3538

GIT_ORIGIN_REV_ID=1bf9e320dc148e1bc959e7577372d3a393f35574
PiperOrigin-RevId: 563258432
  • Loading branch information
pubiqq authored and dsn5ft committed Sep 7, 2023
1 parent b5d6f1a commit 0663019
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 87 deletions.
33 changes: 5 additions & 28 deletions lib/java/com/google/android/material/appbar/AppBarLayout.java
Expand Up @@ -34,7 +34,6 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.ColorStateListDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
Expand All @@ -53,7 +52,6 @@
import android.widget.ScrollView;
import androidx.annotation.ColorInt;
import androidx.annotation.Dimension;
import androidx.annotation.DoNotInline;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.IntDef;
Expand All @@ -77,6 +75,7 @@
import com.google.android.material.animation.AnimationUtils;
import com.google.android.material.animation.ArgbEvaluatorCompat;
import com.google.android.material.appbar.AppBarLayout.BaseBehavior.SavedState;
import com.google.android.material.drawable.DrawableUtils;
import com.google.android.material.internal.ThemeEnforcement;
import com.google.android.material.motion.MotionUtils;
import com.google.android.material.resources.MaterialResources;
Expand Down Expand Up @@ -262,10 +261,11 @@ public AppBarLayout(@NonNull Context context, @Nullable AttributeSet attrs, int
MaterialResources.getColorStateList(
context, a, R.styleable.AppBarLayout_liftOnScrollColor);

ColorStateList backgroundCSL = getBackgroundCSL();
if (backgroundCSL != null) {
ColorStateList backgroundColorStateList =
DrawableUtils.getColorStateListOrNull(getBackground());
if (backgroundColorStateList != null) {
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
materialShapeDrawable.setFillColor(backgroundCSL);
materialShapeDrawable.setFillColor(backgroundColorStateList);
// If there is a lift on scroll color specified, we do not initialize the elevation overlay
// and set the alpha to zero manually.
if (liftOnScrollColor != null) {
Expand Down Expand Up @@ -326,17 +326,6 @@ public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets)
});
}

@Nullable
private ColorStateList getBackgroundCSL() {
Drawable background = getBackground();
if (background instanceof ColorDrawable) {
return ColorStateList.valueOf(((ColorDrawable) background).getColor());
} else if (VERSION.SDK_INT >= VERSION_CODES.Q) {
return DrawableHelperV29.maybeGetBackgroundCSL(background);
}
return null;
}

private void initializeLiftOnScrollWithColor(MaterialShapeDrawable background) {
MaterialShapeDrawable liftBackground = new MaterialShapeDrawable();
liftBackground.setFillColor(liftOnScrollColor);
Expand Down Expand Up @@ -2612,16 +2601,4 @@ public void onOffsetChanged(
}
}
}

@RequiresApi(VERSION_CODES.Q)
private static class DrawableHelperV29 {
@DoNotInline
@Nullable
private static ColorStateList maybeGetBackgroundCSL(@Nullable Drawable background) {
if (background instanceof ColorStateListDrawable) {
return ((ColorStateListDrawable) background).getColorStateList();
}
return null;
}
}
}
23 changes: 12 additions & 11 deletions lib/java/com/google/android/material/appbar/MaterialToolbar.java
Expand Up @@ -25,7 +25,6 @@
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION_CODES;
import androidx.appcompat.view.menu.MenuBuilder;
Expand All @@ -34,7 +33,6 @@
import android.util.Pair;
import android.view.Menu;
import android.view.View;
import android.view.View.MeasureSpec;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.ColorInt;
Expand All @@ -43,6 +41,7 @@
import androidx.annotation.RequiresApi;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.view.ViewCompat;
import com.google.android.material.drawable.DrawableUtils;
import com.google.android.material.internal.ThemeEnforcement;
import com.google.android.material.internal.ToolbarUtils;
import com.google.android.material.shape.MaterialShapeDrawable;
Expand Down Expand Up @@ -378,16 +377,18 @@ public boolean isSubtitleCentered() {

private void initBackground(Context context) {
Drawable background = getBackground();
if (background != null && !(background instanceof ColorDrawable)) {
return;
ColorStateList backgroundColorStateList =
background == null
? ColorStateList.valueOf(Color.TRANSPARENT)
: DrawableUtils.getColorStateListOrNull(background);

if (backgroundColorStateList != null) {
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
materialShapeDrawable.setFillColor(backgroundColorStateList);
materialShapeDrawable.initializeElevationOverlay(context);
materialShapeDrawable.setElevation(ViewCompat.getElevation(this));
ViewCompat.setBackground(this, materialShapeDrawable);
}
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
int backgroundColor =
background != null ? ((ColorDrawable) background).getColor() : Color.TRANSPARENT;
materialShapeDrawable.setFillColor(ColorStateList.valueOf(backgroundColor));
materialShapeDrawable.initializeElevationOverlay(context);
materialShapeDrawable.setElevation(ViewCompat.getElevation(this));
ViewCompat.setBackground(this, materialShapeDrawable);
}

@Nullable
Expand Down
Expand Up @@ -24,7 +24,6 @@
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
Expand All @@ -50,6 +49,7 @@
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import com.google.android.material.internal.EdgeToEdgeUtils;
import com.google.android.material.internal.ViewUtils;
import com.google.android.material.motion.MaterialBackOrchestrator;
import com.google.android.material.shape.MaterialShapeDrawable;

Expand Down Expand Up @@ -467,12 +467,15 @@ private EdgeToEdgeCallback(
if (backgroundTint != null) {
// First check for a tint
lightBottomSheet = isColorLight(backgroundTint.getDefaultColor());
} else if (bottomSheet.getBackground() instanceof ColorDrawable) {
// Then check for the background color
lightBottomSheet = isColorLight(((ColorDrawable) bottomSheet.getBackground()).getColor());
} else {
// Otherwise don't change the status bar color
lightBottomSheet = null;
Integer backgroundColor = ViewUtils.getBackgroundColor(bottomSheet);
if (backgroundColor != null) {
// Then check for the background color
lightBottomSheet = isColorLight(backgroundColor);
} else {
// Otherwise don't change the status bar color
lightBottomSheet = null;
}
}
}

Expand Down
30 changes: 30 additions & 0 deletions lib/java/com/google/android/material/drawable/DrawableUtils.java
Expand Up @@ -29,6 +29,8 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.ColorStateListDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.RippleDrawable;
Expand Down Expand Up @@ -369,4 +371,32 @@ public static void setOutlineToPath(@NonNull final Outline outline, @NonNull fin
outline.setConvexPath(path);
}
}

/**
* Returns the {@link ColorStateList} if it can be retrieved from the {@code drawable}, or null
* otherwise.
*
* <p>In particular:
*
* <ul>
* <li>If the {@code drawable} is a {@link ColorStateListDrawable}, the method will return the
* {@code drawable}'s {@link ColorStateList}.
* <li>If the {@code drawable} is a {@link ColorDrawable}, the method will return a {@link
* ColorStateList} containing the {@code drawable}'s color.
* </ul>
*/
@Nullable
public static ColorStateList getColorStateListOrNull(@Nullable final Drawable drawable) {
if (drawable instanceof ColorDrawable) {
return ColorStateList.valueOf(((ColorDrawable) drawable).getColor());
}

if (VERSION.SDK_INT >= VERSION_CODES.Q) {
if (drawable instanceof ColorStateListDrawable) {
return ((ColorStateListDrawable) drawable).getColorStateList();
}
}

return null;
}
}
22 changes: 17 additions & 5 deletions lib/java/com/google/android/material/internal/ViewUtils.java
Expand Up @@ -22,11 +22,13 @@
import static androidx.core.content.ContextCompat.getSystemService;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.ColorStateListDrawable;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
Expand All @@ -48,6 +50,7 @@
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import com.google.android.material.drawable.DrawableUtils;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -436,13 +439,22 @@ public static void removeOnGlobalLayoutListener(
}

/**
* Returns the provided view's background color, if it has ColorDrawable as its background, or
* {@code null} if the background has a different drawable type.
* Returns the color if it can be retrieved from the {@code view}'s background drawable, or null
* otherwise.
*
* <p>In particular:
*
* <ul>
* <li>If the {@code view}'s background drawable is a {@link ColorDrawable}, the method will
* return the drawable's color.
* <li>If the {@code view}'s background drawable is a {@link ColorStateListDrawable}, the method
* will return the default color of the drawable's {@link ColorStateList}.
* </ul>
*/
@Nullable
public static Integer getBackgroundColor(@NonNull View view) {
return view.getBackground() instanceof ColorDrawable
? ((ColorDrawable) view.getBackground()).getColor()
: null;
final ColorStateList backgroundColorStateList =
DrawableUtils.getColorStateListOrNull(view.getBackground());
return backgroundColorStateList != null ? backgroundColorStateList.getDefaultColor() : null;
}
}
Expand Up @@ -25,7 +25,6 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
Expand Down Expand Up @@ -56,6 +55,7 @@
import androidx.core.view.ViewCompat;
import androidx.customview.view.AbsSavedState;
import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.drawable.DrawableUtils;
import com.google.android.material.internal.ThemeEnforcement;
import com.google.android.material.resources.MaterialResources;
import com.google.android.material.shape.MaterialShapeDrawable;
Expand Down Expand Up @@ -195,10 +195,20 @@ public NavigationBarView(
setItemTextColor(attributes.getColorStateList(R.styleable.NavigationBarView_itemTextColor));
}

if (getBackground() == null || getBackground() instanceof ColorDrawable) {
// Add a MaterialShapeDrawable as background that supports tinting in every API level.
ViewCompat.setBackground(this, createMaterialShapeDrawableBackground(context,
ShapeAppearanceModel.builder(context, attrs, defStyleAttr, defStyleRes).build()));
// Add a MaterialShapeDrawable as background that supports tinting in every API level.
Drawable background = getBackground();
ColorStateList backgroundColorStateList = DrawableUtils.getColorStateListOrNull(background);

if (background == null || backgroundColorStateList != null) {
ShapeAppearanceModel shapeAppearanceModel =
ShapeAppearanceModel.builder(context, attrs, defStyleAttr, defStyleRes).build();
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable(shapeAppearanceModel);
if (backgroundColorStateList != null) {
// Setting fill color with a transparent CSL will disable the tint list.
materialShapeDrawable.setFillColor(backgroundColorStateList);
}
materialShapeDrawable.initializeElevationOverlay(context);
ViewCompat.setBackground(this, materialShapeDrawable);
}

if (attributes.hasValue(R.styleable.NavigationBarView_itemPaddingTop)) {
Expand Down Expand Up @@ -306,20 +316,6 @@ public void onMenuModeChange(MenuBuilder menu) {}
});
}

@NonNull
private MaterialShapeDrawable createMaterialShapeDrawableBackground(
Context context, ShapeAppearanceModel shapeAppearanceModel) {
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
Drawable originalBackground = getBackground();
if (originalBackground instanceof ColorDrawable) {
materialShapeDrawable.setFillColor(
ColorStateList.valueOf(((ColorDrawable) originalBackground).getColor()));
}
materialShapeDrawable.initializeElevationOverlay(context);
materialShapeDrawable.setShapeAppearanceModel(shapeAppearanceModel);
return materialShapeDrawable;
}

@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Expand Down
Expand Up @@ -30,8 +30,6 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.ColorStateListDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.RippleDrawable;
Expand Down Expand Up @@ -77,6 +75,7 @@
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener;
import androidx.drawerlayout.widget.DrawerLayout.SimpleDrawerListener;
import com.google.android.material.drawable.DrawableUtils;
import com.google.android.material.internal.ContextUtils;
import com.google.android.material.internal.NavigationMenu;
import com.google.android.material.internal.NavigationMenuPresenter;
Expand Down Expand Up @@ -201,23 +200,17 @@ public NavigationView(@NonNull Context context, @Nullable AttributeSet attrs, in
drawerLayoutCornerSize =
a.getDimensionPixelSize(R.styleable.NavigationView_drawerLayoutCornerSize, 0);

Drawable background = getBackground();
// Set the background to a MaterialShapeDrawable if it hasn't been set or if it can be converted
// to a MaterialShapeDrawable.
if (background == null
|| background instanceof ColorDrawable
|| (VERSION.SDK_INT >= VERSION_CODES.Q
&& background instanceof ColorStateListDrawable)) {
Drawable background = getBackground();
ColorStateList backgroundColorStateList = DrawableUtils.getColorStateListOrNull(background);

if (background == null || backgroundColorStateList != null) {
ShapeAppearanceModel shapeAppearanceModel =
ShapeAppearanceModel.builder(context, attrs, defStyleAttr, DEF_STYLE_RES).build();
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable(shapeAppearanceModel);
if (background instanceof ColorDrawable) {
materialShapeDrawable.setFillColor(
ColorStateList.valueOf(((ColorDrawable) background).getColor()));
}
if (VERSION.SDK_INT >= VERSION_CODES.Q && background instanceof ColorStateListDrawable) {
materialShapeDrawable.setFillColor(
((ColorStateListDrawable) background).getColorStateList());
if (backgroundColorStateList != null) {
materialShapeDrawable.setFillColor(backgroundColorStateList);
}
materialShapeDrawable.initializeElevationOverlay(context);
ViewCompat.setBackground(this, materialShapeDrawable);
Expand Down
8 changes: 4 additions & 4 deletions lib/java/com/google/android/material/tabs/TabLayout.java
Expand Up @@ -36,7 +36,6 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
Expand Down Expand Up @@ -546,10 +545,11 @@ public TabLayout(@NonNull Context context, @Nullable AttributeSet attrs, int def
DEF_STYLE_RES,
R.styleable.TabLayout_tabTextAppearance);

if (getBackground() instanceof ColorDrawable) {
ColorDrawable background = (ColorDrawable) getBackground();
ColorStateList backgroundColorStateList =
DrawableUtils.getColorStateListOrNull(getBackground());
if (backgroundColorStateList != null) {
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
materialShapeDrawable.setFillColor(ColorStateList.valueOf(background.getColor()));
materialShapeDrawable.setFillColor(backgroundColorStateList);
materialShapeDrawable.initializeElevationOverlay(context);
materialShapeDrawable.setElevation(ViewCompat.getElevation(this));
ViewCompat.setBackground(this, materialShapeDrawable);
Expand Down

0 comments on commit 0663019

Please sign in to comment.