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