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