ColorDrawable.java revision 727cae197b123ef764a1f8fbe08a995b000d14c3
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.NonNull;
20import android.graphics.*;
21import android.graphics.PorterDuff.Mode;
22import android.content.res.ColorStateList;
23import android.content.res.Resources;
24import android.content.res.Resources.Theme;
25import android.content.res.TypedArray;
26import android.util.AttributeSet;
27import android.view.ViewDebug;
28
29import com.android.internal.R;
30
31import org.xmlpull.v1.XmlPullParser;
32import org.xmlpull.v1.XmlPullParserException;
33
34import 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>&lt;color></code> element.</p>
41 *
42 * @attr ref android.R.styleable#ColorDrawable_color
43 */
44public 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#L} 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 void applyTheme(Theme t) {
256        super.applyTheme(t);
257
258        final ColorState state = mColorState;
259        if (state == null || state.mThemeAttrs == null) {
260            return;
261        }
262
263        final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ColorDrawable);
264        updateStateFromTypedArray(a);
265        a.recycle();
266    }
267
268    @Override
269    public ConstantState getConstantState() {
270        return mColorState;
271    }
272
273    final static class ColorState extends ConstantState {
274        int[] mThemeAttrs;
275        int mBaseColor; // base color, independent of setAlpha()
276        @ViewDebug.ExportedProperty
277        int mUseColor;  // basecolor modulated by setAlpha()
278        int mChangingConfigurations;
279        ColorStateList mTint = null;
280        Mode mTintMode = DEFAULT_TINT_MODE;
281
282        ColorState() {
283            // Empty constructor.
284        }
285
286        ColorState(ColorState state) {
287            mThemeAttrs = state.mThemeAttrs;
288            mBaseColor = state.mBaseColor;
289            mUseColor = state.mUseColor;
290            mChangingConfigurations = state.mChangingConfigurations;
291            mTint = state.mTint;
292            mTintMode = state.mTintMode;
293        }
294
295        @Override
296        public boolean canApplyTheme() {
297            return mThemeAttrs != null;
298        }
299
300        @Override
301        public Drawable newDrawable() {
302            return new ColorDrawable(this, null, null);
303        }
304
305        @Override
306        public Drawable newDrawable(Resources res) {
307            return new ColorDrawable(this, res, null);
308        }
309
310        @Override
311        public Drawable newDrawable(Resources res, Theme theme) {
312            return new ColorDrawable(this, res, theme);
313        }
314
315        @Override
316        public int getChangingConfigurations() {
317            return mChangingConfigurations;
318        }
319    }
320
321    private ColorDrawable(ColorState state, Resources res, Theme theme) {
322        if (theme != null && state.canApplyTheme()) {
323            mColorState = new ColorState(state);
324            applyTheme(theme);
325        } else {
326            mColorState = state;
327        }
328
329        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
330    }
331}
332