ColorDrawable.java revision 955d8d69ea6caabce1461dc25b339b9bf9dc61a6
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    @Override
92    public void draw(Canvas canvas) {
93        final ColorFilter colorFilter = mPaint.getColorFilter();
94        if ((mColorState.mUseColor >>> 24) != 0 || colorFilter != null || mTintFilter != null) {
95            if (colorFilter == null) {
96                mPaint.setColorFilter(mTintFilter);
97            }
98
99            mPaint.setColor(mColorState.mUseColor);
100            canvas.drawRect(getBounds(), mPaint);
101
102            // Restore original color filter.
103            mPaint.setColorFilter(colorFilter);
104        }
105    }
106
107    /**
108     * Gets the drawable's color value.
109     *
110     * @return int The color to draw.
111     */
112    public int getColor() {
113        return mColorState.mUseColor;
114    }
115
116    /**
117     * Sets the drawable's color value. This action will clobber the results of
118     * prior calls to {@link #setAlpha(int)} on this object, which side-affected
119     * the underlying color.
120     *
121     * @param color The color to draw.
122     */
123    public void setColor(int color) {
124        if (mColorState.mBaseColor != color || mColorState.mUseColor != color) {
125            mColorState.mBaseColor = mColorState.mUseColor = color;
126            invalidateSelf();
127        }
128    }
129
130    /**
131     * Returns the alpha value of this drawable's color.
132     *
133     * @return A value between 0 and 255.
134     */
135    @Override
136    public int getAlpha() {
137        return mColorState.mUseColor >>> 24;
138    }
139
140    /**
141     * Sets the color's alpha value.
142     *
143     * @param alpha The alpha value to set, between 0 and 255.
144     */
145    @Override
146    public void setAlpha(int alpha) {
147        alpha += alpha >> 7;   // make it 0..256
148        final int baseAlpha = mColorState.mBaseColor >>> 24;
149        final int useAlpha = baseAlpha * alpha >> 8;
150        final int useColor = (mColorState.mBaseColor << 8 >>> 8) | (useAlpha << 24);
151        if (mColorState.mUseColor != useColor) {
152            mColorState.mUseColor = useColor;
153            invalidateSelf();
154        }
155    }
156
157    /**
158     * Sets the color filter applied to this color.
159     * <p>
160     * Only supported on version {@link android.os.Build.VERSION_CODES#LOLLIPOP} and
161     * above. Calling this method has no effect on earlier versions.
162     *
163     * @see android.graphics.drawable.Drawable#setColorFilter(ColorFilter)
164     */
165    @Override
166    public void setColorFilter(ColorFilter colorFilter) {
167        mPaint.setColorFilter(colorFilter);
168    }
169
170    @Override
171    public void setTintList(ColorStateList tint) {
172        mColorState.mTint = tint;
173        mTintFilter = updateTintFilter(mTintFilter, tint, mColorState.mTintMode);
174        invalidateSelf();
175    }
176
177    @Override
178    public void setTintMode(Mode tintMode) {
179        mColorState.mTintMode = tintMode;
180        mTintFilter = updateTintFilter(mTintFilter, mColorState.mTint, tintMode);
181        invalidateSelf();
182    }
183
184    @Override
185    protected boolean onStateChange(int[] stateSet) {
186        final ColorState state = mColorState;
187        if (state.mTint != null && state.mTintMode != null) {
188            mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
189            return true;
190        }
191        return false;
192    }
193
194    @Override
195    public boolean isStateful() {
196        return mColorState.mTint != null && mColorState.mTint.isStateful();
197    }
198
199    @Override
200    public int getOpacity() {
201        if (mTintFilter != null || mPaint.getColorFilter() != null) {
202            return PixelFormat.TRANSLUCENT;
203        }
204
205        switch (mColorState.mUseColor >>> 24) {
206            case 255:
207                return PixelFormat.OPAQUE;
208            case 0:
209                return PixelFormat.TRANSPARENT;
210        }
211        return PixelFormat.TRANSLUCENT;
212    }
213
214    @Override
215    public void getOutline(@NonNull Outline outline) {
216        outline.setRect(getBounds());
217        outline.setAlpha(getAlpha() / 255.0f);
218    }
219
220    @Override
221    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
222            throws XmlPullParserException, IOException {
223        super.inflate(r, parser, attrs, theme);
224
225        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable);
226        updateStateFromTypedArray(a);
227        a.recycle();
228    }
229
230    /**
231     * Updates the constant state from the values in the typed array.
232     */
233    private void updateStateFromTypedArray(TypedArray a) {
234        final ColorState state = mColorState;
235
236        // Account for any configuration changes.
237        state.mChangingConfigurations |= a.getChangingConfigurations();
238
239        // Extract the theme attributes, if any.
240        state.mThemeAttrs = a.extractThemeAttrs();
241
242        state.mBaseColor = a.getColor(R.styleable.ColorDrawable_color, state.mBaseColor);
243        state.mUseColor = state.mBaseColor;
244    }
245
246    @Override
247    public void applyTheme(Theme t) {
248        super.applyTheme(t);
249
250        final ColorState state = mColorState;
251        if (state == null || state.mThemeAttrs == null) {
252            return;
253        }
254
255        final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ColorDrawable);
256        updateStateFromTypedArray(a);
257        a.recycle();
258    }
259
260    @Override
261    public ConstantState getConstantState() {
262        return mColorState;
263    }
264
265    final static class ColorState extends ConstantState {
266        int[] mThemeAttrs;
267        int mBaseColor; // base color, independent of setAlpha()
268        @ViewDebug.ExportedProperty
269        int mUseColor;  // basecolor modulated by setAlpha()
270        int mChangingConfigurations;
271        ColorStateList mTint = null;
272        Mode mTintMode = DEFAULT_TINT_MODE;
273
274        ColorState() {
275            // Empty constructor.
276        }
277
278        ColorState(ColorState state) {
279            mThemeAttrs = state.mThemeAttrs;
280            mBaseColor = state.mBaseColor;
281            mUseColor = state.mUseColor;
282            mChangingConfigurations = state.mChangingConfigurations;
283            mTint = state.mTint;
284            mTintMode = state.mTintMode;
285        }
286
287        @Override
288        public boolean canApplyTheme() {
289            return mThemeAttrs != null;
290        }
291
292        @Override
293        public Drawable newDrawable() {
294            return new ColorDrawable(this, null, null);
295        }
296
297        @Override
298        public Drawable newDrawable(Resources res) {
299            return new ColorDrawable(this, res, null);
300        }
301
302        @Override
303        public Drawable newDrawable(Resources res, Theme theme) {
304            return new ColorDrawable(this, res, theme);
305        }
306
307        @Override
308        public int getChangingConfigurations() {
309            return mChangingConfigurations;
310        }
311    }
312
313    private ColorDrawable(ColorState state, Resources res, Theme theme) {
314        if (theme != null && state.canApplyTheme()) {
315            mColorState = new ColorState(state);
316            applyTheme(theme);
317        } else {
318            mColorState = state;
319        }
320
321        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
322    }
323}
324