NinePatchDrawable.java revision 373954a1fd371a23c420aeeea9ccbc4d983733d7
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
17package android.graphics.drawable;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.content.res.ColorStateList;
22import android.content.res.Resources;
23import android.content.res.Resources.Theme;
24import android.content.res.TypedArray;
25import android.graphics.Bitmap;
26import android.graphics.BitmapFactory;
27import android.graphics.Canvas;
28import android.graphics.ColorFilter;
29import android.graphics.Insets;
30import android.graphics.NinePatch;
31import android.graphics.Outline;
32import android.graphics.Paint;
33import android.graphics.PixelFormat;
34import android.graphics.PorterDuff;
35import android.graphics.PorterDuff.Mode;
36import android.graphics.PorterDuffColorFilter;
37import android.graphics.Rect;
38import android.graphics.Region;
39import android.util.AttributeSet;
40import android.util.DisplayMetrics;
41import android.util.LayoutDirection;
42import android.util.TypedValue;
43
44import com.android.internal.R;
45
46import org.xmlpull.v1.XmlPullParser;
47import org.xmlpull.v1.XmlPullParserException;
48
49import java.io.IOException;
50import java.io.InputStream;
51import 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 */
66public 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.getChangingConfigurations();
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 colorFilter) {
342        if (mPaint == null && colorFilter == null) {
343            // Fast common case -- leave at no color filter.
344            return;
345        }
346        getPaint().setColorFilter(colorFilter);
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 boolean isDither() {
378        return mPaint == null ? DEFAULT_DITHER : mPaint.isDither();
379    }
380
381    @Override
382    public void setAutoMirrored(boolean mirrored) {
383        mNinePatchState.mAutoMirrored = mirrored;
384    }
385
386    private boolean needsMirroring() {
387        return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
388    }
389
390    @Override
391    public boolean isAutoMirrored() {
392        return mNinePatchState.mAutoMirrored;
393    }
394
395    @Override
396    public void setFilterBitmap(boolean filter) {
397        getPaint().setFilterBitmap(filter);
398        invalidateSelf();
399    }
400
401    @Override
402    public boolean isFilterBitmap() {
403        if (mPaint == null) {
404            return false;
405        }
406        return getPaint().isFilterBitmap();
407    }
408
409    @Override
410    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
411            throws XmlPullParserException, IOException {
412        super.inflate(r, parser, attrs, theme);
413
414        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.NinePatchDrawable);
415        updateStateFromTypedArray(a);
416        a.recycle();
417
418        updateLocalState(r);
419    }
420
421    /**
422     * Updates the constant state from the values in the typed array.
423     */
424    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
425        final Resources r = a.getResources();
426        final NinePatchState state = mNinePatchState;
427
428        // Account for any configuration changes.
429        state.mChangingConfigurations |= a.getChangingConfigurations();
430
431        // Extract the theme attributes, if any.
432        state.mThemeAttrs = a.extractThemeAttrs();
433
434        state.mDither = a.getBoolean(R.styleable.NinePatchDrawable_dither, state.mDither);
435
436        final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
437        if (srcResId != 0) {
438            final BitmapFactory.Options options = new BitmapFactory.Options();
439            options.inDither = !state.mDither;
440            options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
441
442            final Rect padding = new Rect();
443            final Rect opticalInsets = new Rect();
444            Bitmap bitmap = null;
445
446            try {
447                final TypedValue value = new TypedValue();
448                final InputStream is = r.openRawResource(srcResId, value);
449
450                bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
451
452                is.close();
453            } catch (IOException e) {
454                // Ignore
455            }
456
457            if (bitmap == null) {
458                throw new XmlPullParserException(a.getPositionDescription() +
459                        ": <nine-patch> requires a valid src attribute");
460            } else if (bitmap.getNinePatchChunk() == null) {
461                throw new XmlPullParserException(a.getPositionDescription() +
462                        ": <nine-patch> requires a valid 9-patch source image");
463            }
464
465            bitmap.getOpticalInsets(opticalInsets);
466
467            state.mNinePatch = new NinePatch(bitmap, bitmap.getNinePatchChunk());
468            state.mPadding = padding;
469            state.mOpticalInsets = Insets.of(opticalInsets);
470        }
471
472        state.mAutoMirrored = a.getBoolean(
473                R.styleable.NinePatchDrawable_autoMirrored, state.mAutoMirrored);
474        state.mBaseAlpha = a.getFloat(R.styleable.NinePatchDrawable_alpha, state.mBaseAlpha);
475
476        final int tintMode = a.getInt(R.styleable.NinePatchDrawable_tintMode, -1);
477        if (tintMode != -1) {
478            state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
479        }
480
481        final ColorStateList tint = a.getColorStateList(R.styleable.NinePatchDrawable_tint);
482        if (tint != null) {
483            state.mTint = tint;
484        }
485
486        final int densityDpi = r.getDisplayMetrics().densityDpi;
487        state.mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
488    }
489
490    @Override
491    public void applyTheme(Theme t) {
492        super.applyTheme(t);
493
494        final NinePatchState state = mNinePatchState;
495        if (state == null) {
496            return;
497        }
498
499        if (state.mThemeAttrs != null) {
500            final TypedArray a = t.resolveAttributes(
501                    state.mThemeAttrs, R.styleable.NinePatchDrawable);
502            try {
503                updateStateFromTypedArray(a);
504            } catch (XmlPullParserException e) {
505                throw new RuntimeException(e);
506            } finally {
507                a.recycle();
508            }
509        }
510
511        if (state.mTint != null && state.mTint.canApplyTheme()) {
512            state.mTint = state.mTint.obtainForTheme(t);
513        }
514
515        updateLocalState(t.getResources());
516    }
517
518    @Override
519    public boolean canApplyTheme() {
520        return mNinePatchState != null && mNinePatchState.canApplyTheme();
521    }
522
523    public Paint getPaint() {
524        if (mPaint == null) {
525            mPaint = new Paint();
526            mPaint.setDither(DEFAULT_DITHER);
527        }
528        return mPaint;
529    }
530
531    /**
532     * Retrieves the width of the source .png file (before resizing).
533     */
534    @Override
535    public int getIntrinsicWidth() {
536        return mBitmapWidth;
537    }
538
539    /**
540     * Retrieves the height of the source .png file (before resizing).
541     */
542    @Override
543    public int getIntrinsicHeight() {
544        return mBitmapHeight;
545    }
546
547    @Override
548    public int getMinimumWidth() {
549        return mBitmapWidth;
550    }
551
552    @Override
553    public int getMinimumHeight() {
554        return mBitmapHeight;
555    }
556
557    /**
558     * Returns a {@link android.graphics.PixelFormat graphics.PixelFormat}
559     * value of OPAQUE or TRANSLUCENT.
560     */
561    @Override
562    public int getOpacity() {
563        return mNinePatch.hasAlpha() || (mPaint != null && mPaint.getAlpha() < 255) ?
564                PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
565    }
566
567    @Override
568    public Region getTransparentRegion() {
569        return mNinePatch.getTransparentRegion(getBounds());
570    }
571
572    @Override
573    public ConstantState getConstantState() {
574        mNinePatchState.mChangingConfigurations = getChangingConfigurations();
575        return mNinePatchState;
576    }
577
578    @Override
579    public Drawable mutate() {
580        if (!mMutated && super.mutate() == this) {
581            mNinePatchState = new NinePatchState(mNinePatchState);
582            mNinePatch = mNinePatchState.mNinePatch;
583            mMutated = true;
584        }
585        return this;
586    }
587
588    /**
589     * @hide
590     */
591    public void clearMutated() {
592        super.clearMutated();
593        mMutated = false;
594    }
595
596    @Override
597    protected boolean onStateChange(int[] stateSet) {
598        final NinePatchState state = mNinePatchState;
599        if (state.mTint != null && state.mTintMode != null) {
600            mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
601            return true;
602        }
603
604        return false;
605    }
606
607    @Override
608    public boolean isStateful() {
609        final NinePatchState s = mNinePatchState;
610        return super.isStateful() || (s.mTint != null && s.mTint.isStateful());
611    }
612
613    final static class NinePatchState extends ConstantState {
614        // Values loaded during inflation.
615        int[] mThemeAttrs = null;
616        NinePatch mNinePatch = null;
617        ColorStateList mTint = null;
618        Mode mTintMode = DEFAULT_TINT_MODE;
619        Rect mPadding = null;
620        Insets mOpticalInsets = Insets.NONE;
621        float mBaseAlpha = 1.0f;
622        boolean mDither = DEFAULT_DITHER;
623        int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
624        boolean mAutoMirrored = false;
625
626        int mChangingConfigurations;
627
628        NinePatchState() {
629            // Empty constructor.
630        }
631
632        NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding) {
633            this(ninePatch, padding, null, DEFAULT_DITHER, false);
634        }
635
636        NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding,
637                @Nullable Rect opticalInsets) {
638            this(ninePatch, padding, opticalInsets, DEFAULT_DITHER, false);
639        }
640
641        NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding,
642                @Nullable Rect opticalInsets, boolean dither, boolean autoMirror) {
643            mNinePatch = ninePatch;
644            mPadding = padding;
645            mOpticalInsets = Insets.of(opticalInsets);
646            mDither = dither;
647            mAutoMirrored = autoMirror;
648        }
649
650        // Copy constructor
651
652        NinePatchState(@NonNull NinePatchState state) {
653            // We don't deep-copy any fields because they are all immutable.
654            mNinePatch = state.mNinePatch;
655            mTint = state.mTint;
656            mTintMode = state.mTintMode;
657            mThemeAttrs = state.mThemeAttrs;
658            mPadding = state.mPadding;
659            mOpticalInsets = state.mOpticalInsets;
660            mBaseAlpha = state.mBaseAlpha;
661            mDither = state.mDither;
662            mChangingConfigurations = state.mChangingConfigurations;
663            mTargetDensity = state.mTargetDensity;
664            mAutoMirrored = state.mAutoMirrored;
665        }
666
667        @Override
668        public boolean canApplyTheme() {
669            return mThemeAttrs != null
670                    || (mTint != null && mTint.canApplyTheme());
671        }
672
673        @Override
674        public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
675            final Bitmap bitmap = mNinePatch.getBitmap();
676            if (isAtlasable(bitmap) && atlasList.add(bitmap)) {
677                return bitmap.getWidth() * bitmap.getHeight();
678            }
679            return 0;
680        }
681
682        @Override
683        public Drawable newDrawable() {
684            return new NinePatchDrawable(this, null);
685        }
686
687        @Override
688        public Drawable newDrawable(Resources res) {
689            return new NinePatchDrawable(this, res);
690        }
691
692        @Override
693        public int getChangingConfigurations() {
694            return mChangingConfigurations
695                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
696        }
697    }
698
699    /**
700     * The one constructor to rule them all. This is called by all public
701     * constructors to set the state and initialize local properties.
702     */
703    private NinePatchDrawable(NinePatchState state, Resources res) {
704        mNinePatchState = state;
705
706        updateLocalState(res);
707
708        // Push density applied by setNinePatchState into state.
709        mNinePatchState.mTargetDensity = mTargetDensity;
710    }
711
712    /**
713     * Initializes local dynamic properties from state.
714     */
715    private void updateLocalState(Resources res) {
716        final NinePatchState state = mNinePatchState;
717
718        if (res != null) {
719            final int densityDpi = res.getDisplayMetrics().densityDpi;
720            mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
721        } else {
722            mTargetDensity = state.mTargetDensity;
723        }
724
725
726        // If we can, avoid calling any methods that initialize Paint.
727        if (state.mDither != DEFAULT_DITHER) {
728            setDither(state.mDither);
729        }
730
731        // Make a local copy of the padding.
732        if (state.mPadding != null) {
733            mPadding = new Rect(state.mPadding);
734        }
735
736        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
737        setNinePatch(state.mNinePatch);
738    }
739}
740