1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics.drawable; 18 19 import android.annotation.NonNull; 20 import android.graphics.*; 21 import android.graphics.PorterDuff.Mode; 22 import android.content.res.ColorStateList; 23 import android.content.res.Resources; 24 import android.content.res.Resources.Theme; 25 import android.content.res.TypedArray; 26 import android.util.AttributeSet; 27 import android.view.ViewDebug; 28 29 import com.android.internal.R; 30 31 import org.xmlpull.v1.XmlPullParser; 32 import org.xmlpull.v1.XmlPullParserException; 33 34 import java.io.IOException; 35 36 /** 37 * A specialized Drawable that fills the Canvas with a specified color. 38 * Note that a ColorDrawable ignores the ColorFilter. 39 * 40 * <p>It can be defined in an XML file with the <code><color></code> element.</p> 41 * 42 * @attr ref android.R.styleable#ColorDrawable_color 43 */ 44 public class ColorDrawable extends Drawable { 45 private final Paint mPaint = new Paint(); 46 47 @ViewDebug.ExportedProperty(deepExport = true, prefix = "state_") 48 private ColorState mColorState; 49 private PorterDuffColorFilter mTintFilter; 50 51 private boolean mMutated; 52 53 /** 54 * Creates a new black ColorDrawable. 55 */ 56 public ColorDrawable() { 57 mColorState = new ColorState(); 58 } 59 60 /** 61 * Creates a new ColorDrawable with the specified color. 62 * 63 * @param color The color to draw. 64 */ 65 public ColorDrawable(int color) { 66 mColorState = new ColorState(); 67 68 setColor(color); 69 } 70 71 @Override 72 public int getChangingConfigurations() { 73 return super.getChangingConfigurations() | mColorState.mChangingConfigurations; 74 } 75 76 /** 77 * A mutable BitmapDrawable still shares its Bitmap with any other Drawable 78 * that comes from the same resource. 79 * 80 * @return This drawable. 81 */ 82 @Override 83 public Drawable mutate() { 84 if (!mMutated && super.mutate() == this) { 85 mColorState = new ColorState(mColorState); 86 mMutated = true; 87 } 88 return this; 89 } 90 91 /** 92 * @hide 93 */ 94 public void clearMutated() { 95 super.clearMutated(); 96 mMutated = false; 97 } 98 99 @Override 100 public void draw(Canvas canvas) { 101 final ColorFilter colorFilter = mPaint.getColorFilter(); 102 if ((mColorState.mUseColor >>> 24) != 0 || colorFilter != null || mTintFilter != null) { 103 if (colorFilter == null) { 104 mPaint.setColorFilter(mTintFilter); 105 } 106 107 mPaint.setColor(mColorState.mUseColor); 108 canvas.drawRect(getBounds(), mPaint); 109 110 // Restore original color filter. 111 mPaint.setColorFilter(colorFilter); 112 } 113 } 114 115 /** 116 * Gets the drawable's color value. 117 * 118 * @return int The color to draw. 119 */ 120 public int getColor() { 121 return mColorState.mUseColor; 122 } 123 124 /** 125 * Sets the drawable's color value. This action will clobber the results of 126 * prior calls to {@link #setAlpha(int)} on this object, which side-affected 127 * the underlying color. 128 * 129 * @param color The color to draw. 130 */ 131 public void setColor(int color) { 132 if (mColorState.mBaseColor != color || mColorState.mUseColor != color) { 133 mColorState.mBaseColor = mColorState.mUseColor = color; 134 invalidateSelf(); 135 } 136 } 137 138 /** 139 * Returns the alpha value of this drawable's color. 140 * 141 * @return A value between 0 and 255. 142 */ 143 @Override 144 public int getAlpha() { 145 return mColorState.mUseColor >>> 24; 146 } 147 148 /** 149 * Sets the color's alpha value. 150 * 151 * @param alpha The alpha value to set, between 0 and 255. 152 */ 153 @Override 154 public void setAlpha(int alpha) { 155 alpha += alpha >> 7; // make it 0..256 156 final int baseAlpha = mColorState.mBaseColor >>> 24; 157 final int useAlpha = baseAlpha * alpha >> 8; 158 final int useColor = (mColorState.mBaseColor << 8 >>> 8) | (useAlpha << 24); 159 if (mColorState.mUseColor != useColor) { 160 mColorState.mUseColor = useColor; 161 invalidateSelf(); 162 } 163 } 164 165 /** 166 * Sets the color filter applied to this color. 167 * <p> 168 * Only supported on version {@link android.os.Build.VERSION_CODES#LOLLIPOP} and 169 * above. Calling this method has no effect on earlier versions. 170 * 171 * @see android.graphics.drawable.Drawable#setColorFilter(ColorFilter) 172 */ 173 @Override 174 public void setColorFilter(ColorFilter colorFilter) { 175 mPaint.setColorFilter(colorFilter); 176 } 177 178 @Override 179 public void setTintList(ColorStateList tint) { 180 mColorState.mTint = tint; 181 mTintFilter = updateTintFilter(mTintFilter, tint, mColorState.mTintMode); 182 invalidateSelf(); 183 } 184 185 @Override 186 public void setTintMode(Mode tintMode) { 187 mColorState.mTintMode = tintMode; 188 mTintFilter = updateTintFilter(mTintFilter, mColorState.mTint, tintMode); 189 invalidateSelf(); 190 } 191 192 @Override 193 protected boolean onStateChange(int[] stateSet) { 194 final ColorState state = mColorState; 195 if (state.mTint != null && state.mTintMode != null) { 196 mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); 197 return true; 198 } 199 return false; 200 } 201 202 @Override 203 public boolean isStateful() { 204 return mColorState.mTint != null && mColorState.mTint.isStateful(); 205 } 206 207 @Override 208 public int getOpacity() { 209 if (mTintFilter != null || mPaint.getColorFilter() != null) { 210 return PixelFormat.TRANSLUCENT; 211 } 212 213 switch (mColorState.mUseColor >>> 24) { 214 case 255: 215 return PixelFormat.OPAQUE; 216 case 0: 217 return PixelFormat.TRANSPARENT; 218 } 219 return PixelFormat.TRANSLUCENT; 220 } 221 222 @Override 223 public void getOutline(@NonNull Outline outline) { 224 outline.setRect(getBounds()); 225 outline.setAlpha(getAlpha() / 255.0f); 226 } 227 228 @Override 229 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) 230 throws XmlPullParserException, IOException { 231 super.inflate(r, parser, attrs, theme); 232 233 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable); 234 updateStateFromTypedArray(a); 235 a.recycle(); 236 } 237 238 /** 239 * Updates the constant state from the values in the typed array. 240 */ 241 private void updateStateFromTypedArray(TypedArray a) { 242 final ColorState state = mColorState; 243 244 // Account for any configuration changes. 245 state.mChangingConfigurations |= a.getChangingConfigurations(); 246 247 // Extract the theme attributes, if any. 248 state.mThemeAttrs = a.extractThemeAttrs(); 249 250 state.mBaseColor = a.getColor(R.styleable.ColorDrawable_color, state.mBaseColor); 251 state.mUseColor = state.mBaseColor; 252 } 253 254 @Override 255 public boolean canApplyTheme() { 256 return mColorState.canApplyTheme() || super.canApplyTheme(); 257 } 258 259 @Override 260 public void applyTheme(Theme t) { 261 super.applyTheme(t); 262 263 final ColorState state = mColorState; 264 if (state == null || state.mThemeAttrs == null) { 265 return; 266 } 267 268 final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ColorDrawable); 269 updateStateFromTypedArray(a); 270 a.recycle(); 271 } 272 273 @Override 274 public ConstantState getConstantState() { 275 return mColorState; 276 } 277 278 final static class ColorState extends ConstantState { 279 int[] mThemeAttrs; 280 int mBaseColor; // base color, independent of setAlpha() 281 @ViewDebug.ExportedProperty 282 int mUseColor; // basecolor modulated by setAlpha() 283 int mChangingConfigurations; 284 ColorStateList mTint = null; 285 Mode mTintMode = DEFAULT_TINT_MODE; 286 287 ColorState() { 288 // Empty constructor. 289 } 290 291 ColorState(ColorState state) { 292 mThemeAttrs = state.mThemeAttrs; 293 mBaseColor = state.mBaseColor; 294 mUseColor = state.mUseColor; 295 mChangingConfigurations = state.mChangingConfigurations; 296 mTint = state.mTint; 297 mTintMode = state.mTintMode; 298 } 299 300 @Override 301 public boolean canApplyTheme() { 302 return mThemeAttrs != null; 303 } 304 305 @Override 306 public Drawable newDrawable() { 307 return new ColorDrawable(this); 308 } 309 310 @Override 311 public Drawable newDrawable(Resources res) { 312 return new ColorDrawable(this); 313 } 314 315 @Override 316 public int getChangingConfigurations() { 317 return mChangingConfigurations; 318 } 319 } 320 321 private ColorDrawable(ColorState state) { 322 mColorState = state; 323 mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); 324 } 325 } 326