Home | History | Annotate | Download | only in drawable
      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>&lt;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