/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics.drawable; import android.annotation.NonNull; import android.graphics.*; import android.graphics.PorterDuff.Mode; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.ViewDebug; import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; /** * A specialized Drawable that fills the Canvas with a specified color. * Note that a ColorDrawable ignores the ColorFilter. * *

It can be defined in an XML file with the <color> element.

* * @attr ref android.R.styleable#ColorDrawable_color */ public class ColorDrawable extends Drawable { private final Paint mPaint = new Paint(); @ViewDebug.ExportedProperty(deepExport = true, prefix = "state_") private ColorState mColorState; private PorterDuffColorFilter mTintFilter; private boolean mMutated; /** * Creates a new black ColorDrawable. */ public ColorDrawable() { mColorState = new ColorState(); } /** * Creates a new ColorDrawable with the specified color. * * @param color The color to draw. */ public ColorDrawable(int color) { mColorState = new ColorState(); setColor(color); } @Override public int getChangingConfigurations() { return super.getChangingConfigurations() | mColorState.mChangingConfigurations; } /** * A mutable BitmapDrawable still shares its Bitmap with any other Drawable * that comes from the same resource. * * @return This drawable. */ @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { mColorState = new ColorState(mColorState); mMutated = true; } return this; } /** * @hide */ public void clearMutated() { super.clearMutated(); mMutated = false; } @Override public void draw(Canvas canvas) { final ColorFilter colorFilter = mPaint.getColorFilter(); if ((mColorState.mUseColor >>> 24) != 0 || colorFilter != null || mTintFilter != null) { if (colorFilter == null) { mPaint.setColorFilter(mTintFilter); } mPaint.setColor(mColorState.mUseColor); canvas.drawRect(getBounds(), mPaint); // Restore original color filter. mPaint.setColorFilter(colorFilter); } } /** * Gets the drawable's color value. * * @return int The color to draw. */ public int getColor() { return mColorState.mUseColor; } /** * Sets the drawable's color value. This action will clobber the results of * prior calls to {@link #setAlpha(int)} on this object, which side-affected * the underlying color. * * @param color The color to draw. */ public void setColor(int color) { if (mColorState.mBaseColor != color || mColorState.mUseColor != color) { mColorState.mBaseColor = mColorState.mUseColor = color; invalidateSelf(); } } /** * Returns the alpha value of this drawable's color. * * @return A value between 0 and 255. */ @Override public int getAlpha() { return mColorState.mUseColor >>> 24; } /** * Sets the color's alpha value. * * @param alpha The alpha value to set, between 0 and 255. */ @Override public void setAlpha(int alpha) { alpha += alpha >> 7; // make it 0..256 final int baseAlpha = mColorState.mBaseColor >>> 24; final int useAlpha = baseAlpha * alpha >> 8; final int useColor = (mColorState.mBaseColor << 8 >>> 8) | (useAlpha << 24); if (mColorState.mUseColor != useColor) { mColorState.mUseColor = useColor; invalidateSelf(); } } /** * Sets the color filter applied to this color. *

* Only supported on version {@link android.os.Build.VERSION_CODES#LOLLIPOP} and * above. Calling this method has no effect on earlier versions. * * @see android.graphics.drawable.Drawable#setColorFilter(ColorFilter) */ @Override public void setColorFilter(ColorFilter colorFilter) { mPaint.setColorFilter(colorFilter); } @Override public void setTintList(ColorStateList tint) { mColorState.mTint = tint; mTintFilter = updateTintFilter(mTintFilter, tint, mColorState.mTintMode); invalidateSelf(); } @Override public void setTintMode(Mode tintMode) { mColorState.mTintMode = tintMode; mTintFilter = updateTintFilter(mTintFilter, mColorState.mTint, tintMode); invalidateSelf(); } @Override protected boolean onStateChange(int[] stateSet) { final ColorState state = mColorState; if (state.mTint != null && state.mTintMode != null) { mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); return true; } return false; } @Override public boolean isStateful() { return mColorState.mTint != null && mColorState.mTint.isStateful(); } @Override public int getOpacity() { if (mTintFilter != null || mPaint.getColorFilter() != null) { return PixelFormat.TRANSLUCENT; } switch (mColorState.mUseColor >>> 24) { case 255: return PixelFormat.OPAQUE; case 0: return PixelFormat.TRANSPARENT; } return PixelFormat.TRANSLUCENT; } @Override public void getOutline(@NonNull Outline outline) { outline.setRect(getBounds()); outline.setAlpha(getAlpha() / 255.0f); } @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { super.inflate(r, parser, attrs, theme); final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable); updateStateFromTypedArray(a); a.recycle(); } /** * Updates the constant state from the values in the typed array. */ private void updateStateFromTypedArray(TypedArray a) { final ColorState state = mColorState; // Account for any configuration changes. state.mChangingConfigurations |= a.getChangingConfigurations(); // Extract the theme attributes, if any. state.mThemeAttrs = a.extractThemeAttrs(); state.mBaseColor = a.getColor(R.styleable.ColorDrawable_color, state.mBaseColor); state.mUseColor = state.mBaseColor; } @Override public boolean canApplyTheme() { return mColorState.canApplyTheme() || super.canApplyTheme(); } @Override public void applyTheme(Theme t) { super.applyTheme(t); final ColorState state = mColorState; if (state == null || state.mThemeAttrs == null) { return; } final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ColorDrawable); updateStateFromTypedArray(a); a.recycle(); } @Override public ConstantState getConstantState() { return mColorState; } final static class ColorState extends ConstantState { int[] mThemeAttrs; int mBaseColor; // base color, independent of setAlpha() @ViewDebug.ExportedProperty int mUseColor; // basecolor modulated by setAlpha() int mChangingConfigurations; ColorStateList mTint = null; Mode mTintMode = DEFAULT_TINT_MODE; ColorState() { // Empty constructor. } ColorState(ColorState state) { mThemeAttrs = state.mThemeAttrs; mBaseColor = state.mBaseColor; mUseColor = state.mUseColor; mChangingConfigurations = state.mChangingConfigurations; mTint = state.mTint; mTintMode = state.mTintMode; } @Override public boolean canApplyTheme() { return mThemeAttrs != null; } @Override public Drawable newDrawable() { return new ColorDrawable(this); } @Override public Drawable newDrawable(Resources res) { return new ColorDrawable(this); } @Override public int getChangingConfigurations() { return mChangingConfigurations; } } private ColorDrawable(ColorState state) { mColorState = state; mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); } }