Home | History | Annotate | Download | only in drawable
      1 /*
      2  * Copyright (C) 2006 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.content.res.Resources;
     20 import android.content.res.TypedArray;
     21 import android.graphics.Bitmap;
     22 import android.graphics.BitmapFactory;
     23 import android.graphics.BitmapShader;
     24 import android.graphics.Canvas;
     25 import android.graphics.ColorFilter;
     26 import android.graphics.Matrix;
     27 import android.graphics.Paint;
     28 import android.graphics.PixelFormat;
     29 import android.graphics.Rect;
     30 import android.graphics.Shader;
     31 import android.graphics.Xfermode;
     32 import android.util.AttributeSet;
     33 import android.util.DisplayMetrics;
     34 import android.util.LayoutDirection;
     35 import android.view.Gravity;
     36 import org.xmlpull.v1.XmlPullParser;
     37 import org.xmlpull.v1.XmlPullParserException;
     38 
     39 import java.io.IOException;
     40 
     41 /**
     42  * A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a
     43  * BitmapDrawable from a file path, an input stream, through XML inflation, or from
     44  * a {@link android.graphics.Bitmap} object.
     45  * <p>It can be defined in an XML file with the <code>&lt;bitmap></code> element.  For more
     46  * information, see the guide to <a
     47  * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
     48  * <p>
     49  * Also see the {@link android.graphics.Bitmap} class, which handles the management and
     50  * transformation of raw bitmap graphics, and should be used when drawing to a
     51  * {@link android.graphics.Canvas}.
     52  * </p>
     53  *
     54  * @attr ref android.R.styleable#BitmapDrawable_src
     55  * @attr ref android.R.styleable#BitmapDrawable_antialias
     56  * @attr ref android.R.styleable#BitmapDrawable_filter
     57  * @attr ref android.R.styleable#BitmapDrawable_dither
     58  * @attr ref android.R.styleable#BitmapDrawable_gravity
     59  * @attr ref android.R.styleable#BitmapDrawable_mipMap
     60  * @attr ref android.R.styleable#BitmapDrawable_tileMode
     61  */
     62 public class BitmapDrawable extends Drawable {
     63 
     64     private static final int DEFAULT_PAINT_FLAGS =
     65             Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;
     66     private BitmapState mBitmapState;
     67     private Bitmap mBitmap;
     68     private int mTargetDensity;
     69 
     70     private final Rect mDstRect = new Rect();   // Gravity.apply() sets this
     71 
     72     private boolean mApplyGravity;
     73     private boolean mMutated;
     74 
     75      // These are scaled to match the target density.
     76     private int mBitmapWidth;
     77     private int mBitmapHeight;
     78 
     79     // Mirroring matrix for using with Shaders
     80     private Matrix mMirrorMatrix;
     81 
     82     /**
     83      * Create an empty drawable, not dealing with density.
     84      * @deprecated Use {@link #BitmapDrawable(android.content.res.Resources, android.graphics.Bitmap)}
     85      * instead to specify a bitmap to draw with and ensure the correct density is set.
     86      */
     87     @Deprecated
     88     public BitmapDrawable() {
     89         mBitmapState = new BitmapState((Bitmap) null);
     90     }
     91 
     92     /**
     93      * Create an empty drawable, setting initial target density based on
     94      * the display metrics of the resources.
     95      * @deprecated Use {@link #BitmapDrawable(android.content.res.Resources, android.graphics.Bitmap)}
     96      * instead to specify a bitmap to draw with.
     97      */
     98     @Deprecated
     99     @SuppressWarnings({"UnusedParameters"})
    100     public BitmapDrawable(Resources res) {
    101         mBitmapState = new BitmapState((Bitmap) null);
    102         mBitmapState.mTargetDensity = mTargetDensity;
    103     }
    104 
    105     /**
    106      * Create drawable from a bitmap, not dealing with density.
    107      * @deprecated Use {@link #BitmapDrawable(Resources, Bitmap)} to ensure
    108      * that the drawable has correctly set its target density.
    109      */
    110     @Deprecated
    111     public BitmapDrawable(Bitmap bitmap) {
    112         this(new BitmapState(bitmap), null);
    113     }
    114 
    115     /**
    116      * Create drawable from a bitmap, setting initial target density based on
    117      * the display metrics of the resources.
    118      */
    119     public BitmapDrawable(Resources res, Bitmap bitmap) {
    120         this(new BitmapState(bitmap), res);
    121         mBitmapState.mTargetDensity = mTargetDensity;
    122     }
    123 
    124     /**
    125      * Create a drawable by opening a given file path and decoding the bitmap.
    126      * @deprecated Use {@link #BitmapDrawable(Resources, String)} to ensure
    127      * that the drawable has correctly set its target density.
    128      */
    129     @Deprecated
    130     public BitmapDrawable(String filepath) {
    131         this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
    132         if (mBitmap == null) {
    133             android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
    134         }
    135     }
    136 
    137     /**
    138      * Create a drawable by opening a given file path and decoding the bitmap.
    139      */
    140     @SuppressWarnings({"UnusedParameters"})
    141     public BitmapDrawable(Resources res, String filepath) {
    142         this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
    143         mBitmapState.mTargetDensity = mTargetDensity;
    144         if (mBitmap == null) {
    145             android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
    146         }
    147     }
    148 
    149     /**
    150      * Create a drawable by decoding a bitmap from the given input stream.
    151      * @deprecated Use {@link #BitmapDrawable(Resources, java.io.InputStream)} to ensure
    152      * that the drawable has correctly set its target density.
    153      */
    154     @Deprecated
    155     public BitmapDrawable(java.io.InputStream is) {
    156         this(new BitmapState(BitmapFactory.decodeStream(is)), null);
    157         if (mBitmap == null) {
    158             android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
    159         }
    160     }
    161 
    162     /**
    163      * Create a drawable by decoding a bitmap from the given input stream.
    164      */
    165     @SuppressWarnings({"UnusedParameters"})
    166     public BitmapDrawable(Resources res, java.io.InputStream is) {
    167         this(new BitmapState(BitmapFactory.decodeStream(is)), null);
    168         mBitmapState.mTargetDensity = mTargetDensity;
    169         if (mBitmap == null) {
    170             android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
    171         }
    172     }
    173 
    174     /**
    175      * Returns the paint used to render this drawable.
    176      */
    177     public final Paint getPaint() {
    178         return mBitmapState.mPaint;
    179     }
    180 
    181     /**
    182      * Returns the bitmap used by this drawable to render. May be null.
    183      */
    184     public final Bitmap getBitmap() {
    185         return mBitmap;
    186     }
    187 
    188     private void computeBitmapSize() {
    189         mBitmapWidth = mBitmap.getScaledWidth(mTargetDensity);
    190         mBitmapHeight = mBitmap.getScaledHeight(mTargetDensity);
    191     }
    192 
    193     private void setBitmap(Bitmap bitmap) {
    194         if (bitmap != mBitmap) {
    195             mBitmap = bitmap;
    196             if (bitmap != null) {
    197                 computeBitmapSize();
    198             } else {
    199                 mBitmapWidth = mBitmapHeight = -1;
    200             }
    201             invalidateSelf();
    202         }
    203     }
    204 
    205     /**
    206      * Set the density scale at which this drawable will be rendered. This
    207      * method assumes the drawable will be rendered at the same density as the
    208      * specified canvas.
    209      *
    210      * @param canvas The Canvas from which the density scale must be obtained.
    211      *
    212      * @see android.graphics.Bitmap#setDensity(int)
    213      * @see android.graphics.Bitmap#getDensity()
    214      */
    215     public void setTargetDensity(Canvas canvas) {
    216         setTargetDensity(canvas.getDensity());
    217     }
    218 
    219     /**
    220      * Set the density scale at which this drawable will be rendered.
    221      *
    222      * @param metrics The DisplayMetrics indicating the density scale for this drawable.
    223      *
    224      * @see android.graphics.Bitmap#setDensity(int)
    225      * @see android.graphics.Bitmap#getDensity()
    226      */
    227     public void setTargetDensity(DisplayMetrics metrics) {
    228         setTargetDensity(metrics.densityDpi);
    229     }
    230 
    231     /**
    232      * Set the density at which this drawable will be rendered.
    233      *
    234      * @param density The density scale for this drawable.
    235      *
    236      * @see android.graphics.Bitmap#setDensity(int)
    237      * @see android.graphics.Bitmap#getDensity()
    238      */
    239     public void setTargetDensity(int density) {
    240         if (mTargetDensity != density) {
    241             mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
    242             if (mBitmap != null) {
    243                 computeBitmapSize();
    244             }
    245             invalidateSelf();
    246         }
    247     }
    248 
    249     /** Get the gravity used to position/stretch the bitmap within its bounds.
    250      * See android.view.Gravity
    251      * @return the gravity applied to the bitmap
    252      */
    253     public int getGravity() {
    254         return mBitmapState.mGravity;
    255     }
    256 
    257     /** Set the gravity used to position/stretch the bitmap within its bounds.
    258         See android.view.Gravity
    259      * @param gravity the gravity
    260      */
    261     public void setGravity(int gravity) {
    262         if (mBitmapState.mGravity != gravity) {
    263             mBitmapState.mGravity = gravity;
    264             mApplyGravity = true;
    265             invalidateSelf();
    266         }
    267     }
    268 
    269     /**
    270      * Enables or disables the mipmap hint for this drawable's bitmap.
    271      * See {@link Bitmap#setHasMipMap(boolean)} for more information.
    272      *
    273      * If the bitmap is null calling this method has no effect.
    274      *
    275      * @param mipMap True if the bitmap should use mipmaps, false otherwise.
    276      *
    277      * @see #hasMipMap()
    278      */
    279     public void setMipMap(boolean mipMap) {
    280         if (mBitmapState.mBitmap != null) {
    281             mBitmapState.mBitmap.setHasMipMap(mipMap);
    282             invalidateSelf();
    283         }
    284     }
    285 
    286     /**
    287      * Indicates whether the mipmap hint is enabled on this drawable's bitmap.
    288      *
    289      * @return True if the mipmap hint is set, false otherwise. If the bitmap
    290      *         is null, this method always returns false.
    291      *
    292      * @see #setMipMap(boolean)
    293      * @attr ref android.R.styleable#BitmapDrawable_mipMap
    294      */
    295     public boolean hasMipMap() {
    296         return mBitmapState.mBitmap != null && mBitmapState.mBitmap.hasMipMap();
    297     }
    298 
    299     /**
    300      * Enables or disables anti-aliasing for this drawable. Anti-aliasing affects
    301      * the edges of the bitmap only so it applies only when the drawable is rotated.
    302      *
    303      * @param aa True if the bitmap should be anti-aliased, false otherwise.
    304      *
    305      * @see #hasAntiAlias()
    306      */
    307     public void setAntiAlias(boolean aa) {
    308         mBitmapState.mPaint.setAntiAlias(aa);
    309         invalidateSelf();
    310     }
    311 
    312     /**
    313      * Indicates whether anti-aliasing is enabled for this drawable.
    314      *
    315      * @return True if anti-aliasing is enabled, false otherwise.
    316      *
    317      * @see #setAntiAlias(boolean)
    318      */
    319     public boolean hasAntiAlias() {
    320         return mBitmapState.mPaint.isAntiAlias();
    321     }
    322 
    323     @Override
    324     public void setFilterBitmap(boolean filter) {
    325         mBitmapState.mPaint.setFilterBitmap(filter);
    326         invalidateSelf();
    327     }
    328 
    329     @Override
    330     public void setDither(boolean dither) {
    331         mBitmapState.mPaint.setDither(dither);
    332         invalidateSelf();
    333     }
    334 
    335     /**
    336      * Indicates the repeat behavior of this drawable on the X axis.
    337      *
    338      * @return {@link Shader.TileMode#CLAMP} if the bitmap does not repeat,
    339      *         {@link Shader.TileMode#REPEAT} or {@link Shader.TileMode#MIRROR} otherwise.
    340      */
    341     public Shader.TileMode getTileModeX() {
    342         return mBitmapState.mTileModeX;
    343     }
    344 
    345     /**
    346      * Indicates the repeat behavior of this drawable on the Y axis.
    347      *
    348      * @return {@link Shader.TileMode#CLAMP} if the bitmap does not repeat,
    349      *         {@link Shader.TileMode#REPEAT} or {@link Shader.TileMode#MIRROR} otherwise.
    350      */
    351     public Shader.TileMode getTileModeY() {
    352         return mBitmapState.mTileModeY;
    353     }
    354 
    355     /**
    356      * Sets the repeat behavior of this drawable on the X axis. By default, the drawable
    357      * does not repeat its bitmap. Using {@link Shader.TileMode#REPEAT} or
    358      * {@link Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled) if the bitmap
    359      * is smaller than this drawable.
    360      *
    361      * @param mode The repeat mode for this drawable.
    362      *
    363      * @see #setTileModeY(android.graphics.Shader.TileMode)
    364      * @see #setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode)
    365      */
    366     public void setTileModeX(Shader.TileMode mode) {
    367         setTileModeXY(mode, mBitmapState.mTileModeY);
    368     }
    369 
    370     /**
    371      * Sets the repeat behavior of this drawable on the Y axis. By default, the drawable
    372      * does not repeat its bitmap. Using {@link Shader.TileMode#REPEAT} or
    373      * {@link Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled) if the bitmap
    374      * is smaller than this drawable.
    375      *
    376      * @param mode The repeat mode for this drawable.
    377      *
    378      * @see #setTileModeX(android.graphics.Shader.TileMode)
    379      * @see #setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode)
    380      */
    381     public final void setTileModeY(Shader.TileMode mode) {
    382         setTileModeXY(mBitmapState.mTileModeX, mode);
    383     }
    384 
    385     /**
    386      * Sets the repeat behavior of this drawable on both axis. By default, the drawable
    387      * does not repeat its bitmap. Using {@link Shader.TileMode#REPEAT} or
    388      * {@link Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled) if the bitmap
    389      * is smaller than this drawable.
    390      *
    391      * @param xmode The X repeat mode for this drawable.
    392      * @param ymode The Y repeat mode for this drawable.
    393      *
    394      * @see #setTileModeX(android.graphics.Shader.TileMode)
    395      * @see #setTileModeY(android.graphics.Shader.TileMode)
    396      */
    397     public void setTileModeXY(Shader.TileMode xmode, Shader.TileMode ymode) {
    398         final BitmapState state = mBitmapState;
    399         if (state.mTileModeX != xmode || state.mTileModeY != ymode) {
    400             state.mTileModeX = xmode;
    401             state.mTileModeY = ymode;
    402             state.mRebuildShader = true;
    403             invalidateSelf();
    404         }
    405     }
    406 
    407     @Override
    408     public void setAutoMirrored(boolean mirrored) {
    409         if (mBitmapState.mAutoMirrored != mirrored) {
    410             mBitmapState.mAutoMirrored = mirrored;
    411             invalidateSelf();
    412         }
    413     }
    414 
    415     @Override
    416     public final boolean isAutoMirrored() {
    417         return mBitmapState.mAutoMirrored;
    418     }
    419 
    420     @Override
    421     public int getChangingConfigurations() {
    422         return super.getChangingConfigurations() | mBitmapState.mChangingConfigurations;
    423     }
    424 
    425     private boolean needMirroring() {
    426         return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
    427     }
    428 
    429     private void updateMirrorMatrix(float dx) {
    430         if (mMirrorMatrix == null) {
    431             mMirrorMatrix = new Matrix();
    432         }
    433         mMirrorMatrix.setTranslate(dx, 0);
    434         mMirrorMatrix.preScale(-1.0f, 1.0f);
    435     }
    436 
    437     @Override
    438     protected void onBoundsChange(Rect bounds) {
    439         super.onBoundsChange(bounds);
    440         mApplyGravity = true;
    441         Shader shader = mBitmapState.mPaint.getShader();
    442         if (shader != null) {
    443             if (needMirroring()) {
    444                 updateMirrorMatrix(bounds.right - bounds.left);
    445                 shader.setLocalMatrix(mMirrorMatrix);
    446             } else {
    447                 if (mMirrorMatrix != null) {
    448                     mMirrorMatrix = null;
    449                     shader.setLocalMatrix(Matrix.IDENTITY_MATRIX);
    450                 }
    451             }
    452         }
    453     }
    454 
    455     @Override
    456     public void draw(Canvas canvas) {
    457         Bitmap bitmap = mBitmap;
    458         if (bitmap != null) {
    459             final BitmapState state = mBitmapState;
    460             if (state.mRebuildShader) {
    461                 Shader.TileMode tmx = state.mTileModeX;
    462                 Shader.TileMode tmy = state.mTileModeY;
    463 
    464                 if (tmx == null && tmy == null) {
    465                     state.mPaint.setShader(null);
    466                 } else {
    467                     state.mPaint.setShader(new BitmapShader(bitmap,
    468                             tmx == null ? Shader.TileMode.CLAMP : tmx,
    469                             tmy == null ? Shader.TileMode.CLAMP : tmy));
    470                 }
    471                 state.mRebuildShader = false;
    472                 copyBounds(mDstRect);
    473             }
    474 
    475             Shader shader = state.mPaint.getShader();
    476             final boolean needMirroring = needMirroring();
    477             if (shader == null) {
    478                 if (mApplyGravity) {
    479                     final int layoutDirection = getLayoutDirection();
    480                     Gravity.apply(state.mGravity, mBitmapWidth, mBitmapHeight,
    481                             getBounds(), mDstRect, layoutDirection);
    482                     mApplyGravity = false;
    483                 }
    484                 if (needMirroring) {
    485                     canvas.save();
    486                     // Mirror the bitmap
    487                     canvas.translate(mDstRect.right - mDstRect.left, 0);
    488                     canvas.scale(-1.0f, 1.0f);
    489                 }
    490                 canvas.drawBitmap(bitmap, null, mDstRect, state.mPaint);
    491                 if (needMirroring) {
    492                     canvas.restore();
    493                 }
    494             } else {
    495                 if (mApplyGravity) {
    496                     copyBounds(mDstRect);
    497                     mApplyGravity = false;
    498                 }
    499                 if (needMirroring) {
    500                     // Mirror the bitmap
    501                     updateMirrorMatrix(mDstRect.right - mDstRect.left);
    502                     shader.setLocalMatrix(mMirrorMatrix);
    503                 } else {
    504                     if (mMirrorMatrix != null) {
    505                         mMirrorMatrix = null;
    506                         shader.setLocalMatrix(Matrix.IDENTITY_MATRIX);
    507                     }
    508                 }
    509                 canvas.drawRect(mDstRect, state.mPaint);
    510             }
    511         }
    512     }
    513 
    514     @Override
    515     public void setAlpha(int alpha) {
    516         int oldAlpha = mBitmapState.mPaint.getAlpha();
    517         if (alpha != oldAlpha) {
    518             mBitmapState.mPaint.setAlpha(alpha);
    519             invalidateSelf();
    520         }
    521     }
    522 
    523     @Override
    524     public int getAlpha() {
    525         return mBitmapState.mPaint.getAlpha();
    526     }
    527 
    528     @Override
    529     public void setColorFilter(ColorFilter cf) {
    530         mBitmapState.mPaint.setColorFilter(cf);
    531         invalidateSelf();
    532     }
    533 
    534     /**
    535      * @hide Candidate for future API inclusion
    536      */
    537     public void setXfermode(Xfermode xfermode) {
    538         mBitmapState.mPaint.setXfermode(xfermode);
    539         invalidateSelf();
    540     }
    541 
    542     /**
    543      * A mutable BitmapDrawable still shares its Bitmap with any other Drawable
    544      * that comes from the same resource.
    545      *
    546      * @return This drawable.
    547      */
    548     @Override
    549     public Drawable mutate() {
    550         if (!mMutated && super.mutate() == this) {
    551             mBitmapState = new BitmapState(mBitmapState);
    552             mMutated = true;
    553         }
    554         return this;
    555     }
    556 
    557     @Override
    558     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
    559             throws XmlPullParserException, IOException {
    560         super.inflate(r, parser, attrs);
    561 
    562         TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.BitmapDrawable);
    563 
    564         final int id = a.getResourceId(com.android.internal.R.styleable.BitmapDrawable_src, 0);
    565         if (id == 0) {
    566             throw new XmlPullParserException(parser.getPositionDescription() +
    567                     ": <bitmap> requires a valid src attribute");
    568         }
    569         final Bitmap bitmap = BitmapFactory.decodeResource(r, id);
    570         if (bitmap == null) {
    571             throw new XmlPullParserException(parser.getPositionDescription() +
    572                     ": <bitmap> requires a valid src attribute");
    573         }
    574         mBitmapState.mBitmap = bitmap;
    575         setBitmap(bitmap);
    576         setTargetDensity(r.getDisplayMetrics());
    577         setMipMap(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_mipMap,
    578                 bitmap.hasMipMap()));
    579         setAutoMirrored(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_autoMirrored,
    580                 false));
    581 
    582         final Paint paint = mBitmapState.mPaint;
    583         paint.setAntiAlias(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_antialias,
    584                 paint.isAntiAlias()));
    585         paint.setFilterBitmap(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_filter,
    586                 paint.isFilterBitmap()));
    587         paint.setDither(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_dither,
    588                 paint.isDither()));
    589         setGravity(a.getInt(com.android.internal.R.styleable.BitmapDrawable_gravity, Gravity.FILL));
    590         int tileMode = a.getInt(com.android.internal.R.styleable.BitmapDrawable_tileMode, -1);
    591         if (tileMode != -1) {
    592             switch (tileMode) {
    593                 case 0:
    594                     setTileModeXY(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    595                     break;
    596                 case 1:
    597                     setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
    598                     break;
    599                 case 2:
    600                     setTileModeXY(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
    601                     break;
    602             }
    603         }
    604 
    605         a.recycle();
    606     }
    607 
    608     @Override
    609     public int getIntrinsicWidth() {
    610         return mBitmapWidth;
    611     }
    612 
    613     @Override
    614     public int getIntrinsicHeight() {
    615         return mBitmapHeight;
    616     }
    617 
    618     @Override
    619     public int getOpacity() {
    620         if (mBitmapState.mGravity != Gravity.FILL) {
    621             return PixelFormat.TRANSLUCENT;
    622         }
    623         Bitmap bm = mBitmap;
    624         return (bm == null || bm.hasAlpha() || mBitmapState.mPaint.getAlpha() < 255) ?
    625                 PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
    626     }
    627 
    628     @Override
    629     public final ConstantState getConstantState() {
    630         mBitmapState.mChangingConfigurations = getChangingConfigurations();
    631         return mBitmapState;
    632     }
    633 
    634     final static class BitmapState extends ConstantState {
    635         Bitmap mBitmap;
    636         int mChangingConfigurations;
    637         int mGravity = Gravity.FILL;
    638         Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS);
    639         Shader.TileMode mTileModeX = null;
    640         Shader.TileMode mTileModeY = null;
    641         int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
    642         boolean mRebuildShader;
    643         boolean mAutoMirrored;
    644 
    645         BitmapState(Bitmap bitmap) {
    646             mBitmap = bitmap;
    647         }
    648 
    649         BitmapState(BitmapState bitmapState) {
    650             this(bitmapState.mBitmap);
    651             mChangingConfigurations = bitmapState.mChangingConfigurations;
    652             mGravity = bitmapState.mGravity;
    653             mTileModeX = bitmapState.mTileModeX;
    654             mTileModeY = bitmapState.mTileModeY;
    655             mTargetDensity = bitmapState.mTargetDensity;
    656             mPaint = new Paint(bitmapState.mPaint);
    657             mRebuildShader = bitmapState.mRebuildShader;
    658             mAutoMirrored = bitmapState.mAutoMirrored;
    659         }
    660 
    661         @Override
    662         public Bitmap getBitmap() {
    663             return mBitmap;
    664         }
    665 
    666         @Override
    667         public Drawable newDrawable() {
    668             return new BitmapDrawable(this, null);
    669         }
    670 
    671         @Override
    672         public Drawable newDrawable(Resources res) {
    673             return new BitmapDrawable(this, res);
    674         }
    675 
    676         @Override
    677         public int getChangingConfigurations() {
    678             return mChangingConfigurations;
    679         }
    680     }
    681 
    682     private BitmapDrawable(BitmapState state, Resources res) {
    683         mBitmapState = state;
    684         if (res != null) {
    685             mTargetDensity = res.getDisplayMetrics().densityDpi;
    686         } else {
    687             mTargetDensity = state.mTargetDensity;
    688         }
    689         setBitmap(state != null ? state.mBitmap : null);
    690     }
    691 }
    692