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.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.content.res.ColorStateList;
     22 import android.content.res.Resources;
     23 import android.content.res.Resources.Theme;
     24 import android.content.res.TypedArray;
     25 import android.graphics.Bitmap;
     26 import android.graphics.BitmapFactory;
     27 import android.graphics.Canvas;
     28 import android.graphics.ColorFilter;
     29 import android.graphics.Insets;
     30 import android.graphics.NinePatch;
     31 import android.graphics.Outline;
     32 import android.graphics.Paint;
     33 import android.graphics.PixelFormat;
     34 import android.graphics.PorterDuff;
     35 import android.graphics.PorterDuff.Mode;
     36 import android.graphics.PorterDuffColorFilter;
     37 import android.graphics.Rect;
     38 import android.graphics.Region;
     39 import android.util.AttributeSet;
     40 import android.util.DisplayMetrics;
     41 import android.util.LayoutDirection;
     42 import android.util.TypedValue;
     43 
     44 import com.android.internal.R;
     45 
     46 import org.xmlpull.v1.XmlPullParser;
     47 import org.xmlpull.v1.XmlPullParserException;
     48 
     49 import java.io.IOException;
     50 import java.io.InputStream;
     51 import java.util.Collection;
     52 
     53 /**
     54  *
     55  * A resizeable bitmap, with stretchable areas that you define. This type of image
     56  * is defined in a .png file with a special format.
     57  *
     58  * <div class="special reference">
     59  * <h3>Developer Guides</h3>
     60  * <p>For more information about how to use a NinePatchDrawable, read the
     61  * <a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">
     62  * Canvas and Drawables</a> developer guide. For information about creating a NinePatch image
     63  * file using the draw9patch tool, see the
     64  * <a href="{@docRoot}guide/developing/tools/draw9patch.html">Draw 9-patch</a> tool guide.</p></div>
     65  */
     66 public class NinePatchDrawable extends Drawable {
     67     // dithering helps a lot, and is pretty cheap, so default is true
     68     private static final boolean DEFAULT_DITHER = false;
     69     private NinePatchState mNinePatchState;
     70     private NinePatch mNinePatch;
     71     private PorterDuffColorFilter mTintFilter;
     72     private Rect mPadding;
     73     private Insets mOpticalInsets = Insets.NONE;
     74     private Paint mPaint;
     75     private boolean mMutated;
     76 
     77     private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
     78 
     79     // These are scaled to match the target density.
     80     private int mBitmapWidth = -1;
     81     private int mBitmapHeight = -1;
     82 
     83     NinePatchDrawable() {
     84         mNinePatchState = new NinePatchState();
     85     }
     86 
     87     /**
     88      * Create drawable from raw nine-patch data, not dealing with density.
     89      * @deprecated Use {@link #NinePatchDrawable(Resources, Bitmap, byte[], Rect, String)}
     90      * to ensure that the drawable has correctly set its target density.
     91      */
     92     @Deprecated
     93     public NinePatchDrawable(Bitmap bitmap, byte[] chunk, Rect padding, String srcName) {
     94         this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), null);
     95     }
     96 
     97     /**
     98      * Create drawable from raw nine-patch data, setting initial target density
     99      * based on the display metrics of the resources.
    100      */
    101     public NinePatchDrawable(Resources res, Bitmap bitmap, byte[] chunk,
    102             Rect padding, String srcName) {
    103         this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), res);
    104         mNinePatchState.mTargetDensity = mTargetDensity;
    105     }
    106 
    107     /**
    108      * Create drawable from raw nine-patch data, setting initial target density
    109      * based on the display metrics of the resources.
    110      *
    111      * @hide
    112      */
    113     public NinePatchDrawable(Resources res, Bitmap bitmap, byte[] chunk,
    114             Rect padding, Rect opticalInsets, String srcName) {
    115         this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding, opticalInsets),
    116                 res);
    117         mNinePatchState.mTargetDensity = mTargetDensity;
    118     }
    119 
    120     /**
    121      * Create drawable from existing nine-patch, not dealing with density.
    122      * @deprecated Use {@link #NinePatchDrawable(Resources, NinePatch)}
    123      * to ensure that the drawable has correctly set its target density.
    124      */
    125     @Deprecated
    126     public NinePatchDrawable(NinePatch patch) {
    127         this(new NinePatchState(patch, new Rect()), null);
    128     }
    129 
    130     /**
    131      * Create drawable from existing nine-patch, setting initial target density
    132      * based on the display metrics of the resources.
    133      */
    134     public NinePatchDrawable(Resources res, NinePatch patch) {
    135         this(new NinePatchState(patch, new Rect()), res);
    136         mNinePatchState.mTargetDensity = mTargetDensity;
    137     }
    138 
    139     /**
    140      * Set the density scale at which this drawable will be rendered. This
    141      * method assumes the drawable will be rendered at the same density as the
    142      * specified canvas.
    143      *
    144      * @param canvas The Canvas from which the density scale must be obtained.
    145      *
    146      * @see android.graphics.Bitmap#setDensity(int)
    147      * @see android.graphics.Bitmap#getDensity()
    148      */
    149     public void setTargetDensity(Canvas canvas) {
    150         setTargetDensity(canvas.getDensity());
    151     }
    152 
    153     /**
    154      * Set the density scale at which this drawable will be rendered.
    155      *
    156      * @param metrics The DisplayMetrics indicating the density scale for this drawable.
    157      *
    158      * @see android.graphics.Bitmap#setDensity(int)
    159      * @see android.graphics.Bitmap#getDensity()
    160      */
    161     public void setTargetDensity(DisplayMetrics metrics) {
    162         setTargetDensity(metrics.densityDpi);
    163     }
    164 
    165     /**
    166      * Set the density at which this drawable will be rendered.
    167      *
    168      * @param density The density scale for this drawable.
    169      *
    170      * @see android.graphics.Bitmap#setDensity(int)
    171      * @see android.graphics.Bitmap#getDensity()
    172      */
    173     public void setTargetDensity(int density) {
    174         if (density != mTargetDensity) {
    175             mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
    176             if (mNinePatch != null) {
    177                 computeBitmapSize();
    178             }
    179             invalidateSelf();
    180         }
    181     }
    182 
    183     private static Insets scaleFromDensity(Insets insets, int sdensity, int tdensity) {
    184         int left = Bitmap.scaleFromDensity(insets.left, sdensity, tdensity);
    185         int top = Bitmap.scaleFromDensity(insets.top, sdensity, tdensity);
    186         int right = Bitmap.scaleFromDensity(insets.right, sdensity, tdensity);
    187         int bottom = Bitmap.scaleFromDensity(insets.bottom, sdensity, tdensity);
    188         return Insets.of(left, top, right, bottom);
    189     }
    190 
    191     private void computeBitmapSize() {
    192         final int sdensity = mNinePatch.getDensity();
    193         final int tdensity = mTargetDensity;
    194         if (sdensity == tdensity) {
    195             mBitmapWidth = mNinePatch.getWidth();
    196             mBitmapHeight = mNinePatch.getHeight();
    197             mOpticalInsets = mNinePatchState.mOpticalInsets;
    198         } else {
    199             mBitmapWidth = Bitmap.scaleFromDensity(mNinePatch.getWidth(), sdensity, tdensity);
    200             mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(), sdensity, tdensity);
    201             if (mNinePatchState.mPadding != null && mPadding != null) {
    202                 Rect dest = mPadding;
    203                 Rect src = mNinePatchState.mPadding;
    204                 if (dest == src) {
    205                     mPadding = dest = new Rect(src);
    206                 }
    207                 dest.left = Bitmap.scaleFromDensity(src.left, sdensity, tdensity);
    208                 dest.top = Bitmap.scaleFromDensity(src.top, sdensity, tdensity);
    209                 dest.right = Bitmap.scaleFromDensity(src.right, sdensity, tdensity);
    210                 dest.bottom = Bitmap.scaleFromDensity(src.bottom, sdensity, tdensity);
    211             }
    212             mOpticalInsets = scaleFromDensity(mNinePatchState.mOpticalInsets, sdensity, tdensity);
    213         }
    214     }
    215 
    216     private void setNinePatch(NinePatch ninePatch) {
    217         if (mNinePatch != ninePatch) {
    218             mNinePatch = ninePatch;
    219             if (ninePatch != null) {
    220                 computeBitmapSize();
    221             } else {
    222                 mBitmapWidth = mBitmapHeight = -1;
    223                 mOpticalInsets = Insets.NONE;
    224             }
    225             invalidateSelf();
    226         }
    227     }
    228 
    229     @Override
    230     public void draw(Canvas canvas) {
    231         final Rect bounds = getBounds();
    232 
    233         final boolean clearColorFilter;
    234         if (mTintFilter != null && getPaint().getColorFilter() == null) {
    235             mPaint.setColorFilter(mTintFilter);
    236             clearColorFilter = true;
    237         } else {
    238             clearColorFilter = false;
    239         }
    240 
    241         final boolean needsMirroring = needsMirroring();
    242         if (needsMirroring) {
    243             // Mirror the 9patch
    244             canvas.translate(bounds.right - bounds.left, 0);
    245             canvas.scale(-1.0f, 1.0f);
    246         }
    247 
    248         final int restoreAlpha;
    249         if (mNinePatchState.mBaseAlpha != 1.0f) {
    250             restoreAlpha = mPaint.getAlpha();
    251             mPaint.setAlpha((int) (restoreAlpha * mNinePatchState.mBaseAlpha + 0.5f));
    252         } else {
    253             restoreAlpha = -1;
    254         }
    255 
    256         mNinePatch.draw(canvas, bounds, mPaint);
    257 
    258         if (clearColorFilter) {
    259             mPaint.setColorFilter(null);
    260         }
    261 
    262         if (restoreAlpha >= 0) {
    263             mPaint.setAlpha(restoreAlpha);
    264         }
    265     }
    266 
    267     @Override
    268     public int getChangingConfigurations() {
    269         return super.getChangingConfigurations() | mNinePatchState.mChangingConfigurations;
    270     }
    271 
    272     @Override
    273     public boolean getPadding(Rect padding) {
    274         final Rect scaledPadding = mPadding;
    275         if (scaledPadding != null) {
    276             if (needsMirroring()) {
    277                 padding.set(scaledPadding.right, scaledPadding.top,
    278                         scaledPadding.left, scaledPadding.bottom);
    279             } else {
    280                 padding.set(scaledPadding);
    281             }
    282             return (padding.left | padding.top | padding.right | padding.bottom) != 0;
    283         }
    284         return false;
    285     }
    286 
    287     @Override
    288     public void getOutline(@NonNull Outline outline) {
    289         final Rect bounds = getBounds();
    290         if (bounds.isEmpty()) return;
    291 
    292         if (mNinePatchState != null) {
    293             NinePatch.InsetStruct insets = mNinePatchState.mNinePatch.getBitmap().getNinePatchInsets();
    294             if (insets != null) {
    295                 final Rect outlineInsets = insets.outlineRect;
    296                 outline.setRoundRect(bounds.left + outlineInsets.left,
    297                         bounds.top + outlineInsets.top,
    298                         bounds.right - outlineInsets.right,
    299                         bounds.bottom - outlineInsets.bottom,
    300                         insets.outlineRadius);
    301                 outline.setAlpha(insets.outlineAlpha * (getAlpha() / 255.0f));
    302                 return;
    303             }
    304         }
    305         super.getOutline(outline);
    306     }
    307 
    308     /**
    309      * @hide
    310      */
    311     @Override
    312     public Insets getOpticalInsets() {
    313         if (needsMirroring()) {
    314             return Insets.of(mOpticalInsets.right, mOpticalInsets.top,
    315                     mOpticalInsets.left, mOpticalInsets.bottom);
    316         } else {
    317             return mOpticalInsets;
    318         }
    319     }
    320 
    321     @Override
    322     public void setAlpha(int alpha) {
    323         if (mPaint == null && alpha == 0xFF) {
    324             // Fast common case -- leave at normal alpha.
    325             return;
    326         }
    327         getPaint().setAlpha(alpha);
    328         invalidateSelf();
    329     }
    330 
    331     @Override
    332     public int getAlpha() {
    333         if (mPaint == null) {
    334             // Fast common case -- normal alpha.
    335             return 0xFF;
    336         }
    337         return getPaint().getAlpha();
    338     }
    339 
    340     @Override
    341     public void setColorFilter(ColorFilter cf) {
    342         if (mPaint == null && cf == null) {
    343             // Fast common case -- leave at no color filter.
    344             return;
    345         }
    346         getPaint().setColorFilter(cf);
    347         invalidateSelf();
    348     }
    349 
    350     @Override
    351     public void setTintList(ColorStateList tint) {
    352         mNinePatchState.mTint = tint;
    353         mTintFilter = updateTintFilter(mTintFilter, tint, mNinePatchState.mTintMode);
    354         invalidateSelf();
    355     }
    356 
    357     @Override
    358     public void setTintMode(PorterDuff.Mode tintMode) {
    359         mNinePatchState.mTintMode = tintMode;
    360         mTintFilter = updateTintFilter(mTintFilter, mNinePatchState.mTint, tintMode);
    361         invalidateSelf();
    362     }
    363 
    364     @Override
    365     public void setDither(boolean dither) {
    366         //noinspection PointlessBooleanExpression
    367         if (mPaint == null && dither == DEFAULT_DITHER) {
    368             // Fast common case -- leave at default dither.
    369             return;
    370         }
    371 
    372         getPaint().setDither(dither);
    373         invalidateSelf();
    374     }
    375 
    376     @Override
    377     public void setAutoMirrored(boolean mirrored) {
    378         mNinePatchState.mAutoMirrored = mirrored;
    379     }
    380 
    381     private boolean needsMirroring() {
    382         return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
    383     }
    384 
    385     @Override
    386     public boolean isAutoMirrored() {
    387         return mNinePatchState.mAutoMirrored;
    388     }
    389 
    390     @Override
    391     public void setFilterBitmap(boolean filter) {
    392         getPaint().setFilterBitmap(filter);
    393         invalidateSelf();
    394     }
    395 
    396     @Override
    397     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
    398             throws XmlPullParserException, IOException {
    399         super.inflate(r, parser, attrs, theme);
    400 
    401         final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.NinePatchDrawable);
    402         updateStateFromTypedArray(a);
    403         a.recycle();
    404     }
    405 
    406     /**
    407      * Updates the constant state from the values in the typed array.
    408      */
    409     private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
    410         final Resources r = a.getResources();
    411         final NinePatchState state = mNinePatchState;
    412 
    413         // Account for any configuration changes.
    414         state.mChangingConfigurations |= a.getChangingConfigurations();
    415 
    416         // Extract the theme attributes, if any.
    417         state.mThemeAttrs = a.extractThemeAttrs();
    418 
    419         state.mDither = a.getBoolean(R.styleable.NinePatchDrawable_dither, state.mDither);
    420 
    421         final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
    422         if (srcResId != 0) {
    423             final BitmapFactory.Options options = new BitmapFactory.Options();
    424             options.inDither = !state.mDither;
    425             options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
    426 
    427             final Rect padding = new Rect();
    428             final Rect opticalInsets = new Rect();
    429             Bitmap bitmap = null;
    430 
    431             try {
    432                 final TypedValue value = new TypedValue();
    433                 final InputStream is = r.openRawResource(srcResId, value);
    434 
    435                 bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
    436 
    437                 is.close();
    438             } catch (IOException e) {
    439                 // Ignore
    440             }
    441 
    442             if (bitmap == null) {
    443                 throw new XmlPullParserException(a.getPositionDescription() +
    444                         ": <nine-patch> requires a valid src attribute");
    445             } else if (bitmap.getNinePatchChunk() == null) {
    446                 throw new XmlPullParserException(a.getPositionDescription() +
    447                         ": <nine-patch> requires a valid 9-patch source image");
    448             }
    449 
    450             bitmap.getOpticalInsets(opticalInsets);
    451 
    452             state.mNinePatch = new NinePatch(bitmap, bitmap.getNinePatchChunk());
    453             state.mPadding = padding;
    454             state.mOpticalInsets = Insets.of(opticalInsets);
    455         }
    456 
    457         state.mAutoMirrored = a.getBoolean(
    458                 R.styleable.NinePatchDrawable_autoMirrored, state.mAutoMirrored);
    459         state.mBaseAlpha = a.getFloat(R.styleable.NinePatchDrawable_alpha, state.mBaseAlpha);
    460 
    461         final int tintMode = a.getInt(R.styleable.NinePatchDrawable_tintMode, -1);
    462         if (tintMode != -1) {
    463             state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
    464         }
    465 
    466         final ColorStateList tint = a.getColorStateList(R.styleable.NinePatchDrawable_tint);
    467         if (tint != null) {
    468             state.mTint = tint;
    469         }
    470 
    471         // Update local properties.
    472         initializeWithState(state, r);
    473 
    474         // Push density applied by setNinePatchState into state.
    475         state.mTargetDensity = mTargetDensity;
    476     }
    477 
    478     @Override
    479     public void applyTheme(Theme t) {
    480         super.applyTheme(t);
    481 
    482         final NinePatchState state = mNinePatchState;
    483         if (state == null || state.mThemeAttrs == null) {
    484             return;
    485         }
    486 
    487         final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.NinePatchDrawable);
    488         try {
    489             updateStateFromTypedArray(a);
    490         } catch (XmlPullParserException e) {
    491             throw new RuntimeException(e);
    492         } finally {
    493             a.recycle();
    494         }
    495     }
    496 
    497     @Override
    498     public boolean canApplyTheme() {
    499         return mNinePatchState != null && mNinePatchState.mThemeAttrs != null;
    500     }
    501 
    502     public Paint getPaint() {
    503         if (mPaint == null) {
    504             mPaint = new Paint();
    505             mPaint.setDither(DEFAULT_DITHER);
    506         }
    507         return mPaint;
    508     }
    509 
    510     /**
    511      * Retrieves the width of the source .png file (before resizing).
    512      */
    513     @Override
    514     public int getIntrinsicWidth() {
    515         return mBitmapWidth;
    516     }
    517 
    518     /**
    519      * Retrieves the height of the source .png file (before resizing).
    520      */
    521     @Override
    522     public int getIntrinsicHeight() {
    523         return mBitmapHeight;
    524     }
    525 
    526     @Override
    527     public int getMinimumWidth() {
    528         return mBitmapWidth;
    529     }
    530 
    531     @Override
    532     public int getMinimumHeight() {
    533         return mBitmapHeight;
    534     }
    535 
    536     /**
    537      * Returns a {@link android.graphics.PixelFormat graphics.PixelFormat}
    538      * value of OPAQUE or TRANSLUCENT.
    539      */
    540     @Override
    541     public int getOpacity() {
    542         return mNinePatch.hasAlpha() || (mPaint != null && mPaint.getAlpha() < 255) ?
    543                 PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
    544     }
    545 
    546     @Override
    547     public Region getTransparentRegion() {
    548         return mNinePatch.getTransparentRegion(getBounds());
    549     }
    550 
    551     @Override
    552     public ConstantState getConstantState() {
    553         mNinePatchState.mChangingConfigurations = getChangingConfigurations();
    554         return mNinePatchState;
    555     }
    556 
    557     @Override
    558     public Drawable mutate() {
    559         if (!mMutated && super.mutate() == this) {
    560             mNinePatchState = new NinePatchState(mNinePatchState);
    561             mNinePatch = mNinePatchState.mNinePatch;
    562             mMutated = true;
    563         }
    564         return this;
    565     }
    566 
    567     /**
    568      * @hide
    569      */
    570     public void clearMutated() {
    571         super.clearMutated();
    572         mMutated = false;
    573     }
    574 
    575     @Override
    576     protected boolean onStateChange(int[] stateSet) {
    577         final NinePatchState state = mNinePatchState;
    578         if (state.mTint != null && state.mTintMode != null) {
    579             mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
    580             return true;
    581         }
    582 
    583         return false;
    584     }
    585 
    586     @Override
    587     public boolean isStateful() {
    588         final NinePatchState s = mNinePatchState;
    589         return super.isStateful() || (s.mTint != null && s.mTint.isStateful());
    590     }
    591 
    592     final static class NinePatchState extends ConstantState {
    593         // Values loaded during inflation.
    594         int[] mThemeAttrs = null;
    595         NinePatch mNinePatch = null;
    596         ColorStateList mTint = null;
    597         Mode mTintMode = DEFAULT_TINT_MODE;
    598         Rect mPadding = null;
    599         Insets mOpticalInsets = Insets.NONE;
    600         float mBaseAlpha = 1.0f;
    601         boolean mDither = DEFAULT_DITHER;
    602         int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
    603         boolean mAutoMirrored = false;
    604 
    605         int mChangingConfigurations;
    606 
    607         NinePatchState() {
    608             // Empty constructor.
    609         }
    610 
    611         NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding) {
    612             this(ninePatch, padding, null, DEFAULT_DITHER, false);
    613         }
    614 
    615         NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding,
    616                 @Nullable Rect opticalInsets) {
    617             this(ninePatch, padding, opticalInsets, DEFAULT_DITHER, false);
    618         }
    619 
    620         NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding,
    621                 @Nullable Rect opticalInsets, boolean dither, boolean autoMirror) {
    622             mNinePatch = ninePatch;
    623             mPadding = padding;
    624             mOpticalInsets = Insets.of(opticalInsets);
    625             mDither = dither;
    626             mAutoMirrored = autoMirror;
    627         }
    628 
    629         // Copy constructor
    630 
    631         NinePatchState(@NonNull NinePatchState state) {
    632             // We don't deep-copy any fields because they are all immutable.
    633             mNinePatch = state.mNinePatch;
    634             mTint = state.mTint;
    635             mTintMode = state.mTintMode;
    636             mThemeAttrs = state.mThemeAttrs;
    637             mPadding = state.mPadding;
    638             mOpticalInsets = state.mOpticalInsets;
    639             mBaseAlpha = state.mBaseAlpha;
    640             mDither = state.mDither;
    641             mChangingConfigurations = state.mChangingConfigurations;
    642             mTargetDensity = state.mTargetDensity;
    643             mAutoMirrored = state.mAutoMirrored;
    644         }
    645 
    646         @Override
    647         public boolean canApplyTheme() {
    648             return mThemeAttrs != null;
    649         }
    650 
    651         @Override
    652         public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
    653             final Bitmap bitmap = mNinePatch.getBitmap();
    654             if (isAtlasable(bitmap) && atlasList.add(bitmap)) {
    655                 return bitmap.getWidth() * bitmap.getHeight();
    656             }
    657             return 0;
    658         }
    659 
    660         @Override
    661         public Drawable newDrawable() {
    662             return new NinePatchDrawable(this, null);
    663         }
    664 
    665         @Override
    666         public Drawable newDrawable(Resources res) {
    667             return new NinePatchDrawable(this, res);
    668         }
    669 
    670         @Override
    671         public int getChangingConfigurations() {
    672             return mChangingConfigurations;
    673         }
    674     }
    675 
    676     /**
    677      * The one constructor to rule them all. This is called by all public
    678      * constructors to set the state and initialize local properties.
    679      */
    680     private NinePatchDrawable(NinePatchState state, Resources res) {
    681         mNinePatchState = state;
    682 
    683         initializeWithState(mNinePatchState, res);
    684     }
    685 
    686     /**
    687      * Initializes local dynamic properties from state.
    688      */
    689     private void initializeWithState(NinePatchState state, Resources res) {
    690         if (res != null) {
    691             mTargetDensity = res.getDisplayMetrics().densityDpi;
    692         } else {
    693             mTargetDensity = state.mTargetDensity;
    694         }
    695 
    696         // If we can, avoid calling any methods that initialize Paint.
    697         if (state.mDither != DEFAULT_DITHER) {
    698             setDither(state.mDither);
    699         }
    700 
    701         // Make a local copy of the padding.
    702         if (state.mPadding != null) {
    703             mPadding = new Rect(state.mPadding);
    704         }
    705 
    706         mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
    707         setNinePatch(state.mNinePatch);
    708     }
    709 }
    710