NinePatchDrawable.java revision c078c605ab904b0e4a5d793cbeffd78c340f2816
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 = Drawable.scaleFromDensity(insets.left, sdensity, tdensity, true);
185        int top = Drawable.scaleFromDensity(insets.top, sdensity, tdensity, true);
186        int right = Drawable.scaleFromDensity(insets.right, sdensity, tdensity, true);
187        int bottom = Drawable.scaleFromDensity(insets.bottom, sdensity, tdensity, true);
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 = Drawable.scaleFromDensity(
200                    mNinePatch.getWidth(), sdensity, tdensity, true);
201            mBitmapHeight = Drawable.scaleFromDensity(
202                    mNinePatch.getHeight(), sdensity, tdensity, true);
203            if (mNinePatchState.mPadding != null && mPadding != null) {
204                Rect dest = mPadding;
205                Rect src = mNinePatchState.mPadding;
206                if (dest == src) {
207                    mPadding = dest = new Rect(src);
208                }
209                dest.left = Drawable.scaleFromDensity(src.left, sdensity, tdensity, true);
210                dest.top = Drawable.scaleFromDensity(src.top, sdensity, tdensity, true);
211                dest.right = Drawable.scaleFromDensity(src.right, sdensity, tdensity, true);
212                dest.bottom = Drawable.scaleFromDensity(src.bottom, sdensity, tdensity, true);
213            }
214            mOpticalInsets = scaleFromDensity(mNinePatchState.mOpticalInsets, sdensity, tdensity);
215        }
216    }
217
218    /**
219     * Sets the nine patch used by this drawable.
220     *
221     * @param ninePatch the nine patch for this drawable
222     */
223    public void setNinePatch(NinePatch ninePatch) {
224        if (mNinePatch != ninePatch) {
225            mNinePatch = ninePatch;
226            if (ninePatch != null) {
227                computeBitmapSize();
228            } else {
229                mBitmapWidth = mBitmapHeight = -1;
230                mOpticalInsets = Insets.NONE;
231            }
232            invalidateSelf();
233        }
234    }
235
236    /**
237     * @return the nine patch used by this drawable
238     */
239    public NinePatch getNinePatch() {
240        return mNinePatch;
241    }
242
243    @Override
244    public void draw(Canvas canvas) {
245        final Rect bounds = getBounds();
246
247        final boolean clearColorFilter;
248        if (mTintFilter != null && getPaint().getColorFilter() == null) {
249            mPaint.setColorFilter(mTintFilter);
250            clearColorFilter = true;
251        } else {
252            clearColorFilter = false;
253        }
254
255        final boolean needsMirroring = needsMirroring();
256        if (needsMirroring) {
257            // Mirror the 9patch
258            canvas.translate(bounds.right - bounds.left, 0);
259            canvas.scale(-1.0f, 1.0f);
260        }
261
262        final int restoreAlpha;
263        if (mNinePatchState.mBaseAlpha != 1.0f) {
264            restoreAlpha = mPaint.getAlpha();
265            mPaint.setAlpha((int) (restoreAlpha * mNinePatchState.mBaseAlpha + 0.5f));
266        } else {
267            restoreAlpha = -1;
268        }
269
270        mNinePatch.draw(canvas, bounds, mPaint);
271
272        if (clearColorFilter) {
273            mPaint.setColorFilter(null);
274        }
275
276        if (restoreAlpha >= 0) {
277            mPaint.setAlpha(restoreAlpha);
278        }
279    }
280
281    @Override
282    public int getChangingConfigurations() {
283        return super.getChangingConfigurations() | mNinePatchState.getChangingConfigurations();
284    }
285
286    @Override
287    public boolean getPadding(Rect padding) {
288        final Rect scaledPadding = mPadding;
289        if (scaledPadding != null) {
290            if (needsMirroring()) {
291                padding.set(scaledPadding.right, scaledPadding.top,
292                        scaledPadding.left, scaledPadding.bottom);
293            } else {
294                padding.set(scaledPadding);
295            }
296            return (padding.left | padding.top | padding.right | padding.bottom) != 0;
297        }
298        return false;
299    }
300
301    @Override
302    public void getOutline(@NonNull Outline outline) {
303        final Rect bounds = getBounds();
304        if (bounds.isEmpty()) return;
305
306        if (mNinePatchState != null) {
307            NinePatch.InsetStruct insets = mNinePatchState.mNinePatch.getBitmap().getNinePatchInsets();
308            if (insets != null) {
309                final Rect outlineInsets = insets.outlineRect;
310                outline.setRoundRect(bounds.left + outlineInsets.left,
311                        bounds.top + outlineInsets.top,
312                        bounds.right - outlineInsets.right,
313                        bounds.bottom - outlineInsets.bottom,
314                        insets.outlineRadius);
315                outline.setAlpha(insets.outlineAlpha * (getAlpha() / 255.0f));
316                return;
317            }
318        }
319        super.getOutline(outline);
320    }
321
322    /**
323     * @hide
324     */
325    @Override
326    public Insets getOpticalInsets() {
327        if (needsMirroring()) {
328            return Insets.of(mOpticalInsets.right, mOpticalInsets.top,
329                    mOpticalInsets.left, mOpticalInsets.bottom);
330        } else {
331            return mOpticalInsets;
332        }
333    }
334
335    @Override
336    public void setAlpha(int alpha) {
337        if (mPaint == null && alpha == 0xFF) {
338            // Fast common case -- leave at normal alpha.
339            return;
340        }
341        getPaint().setAlpha(alpha);
342        invalidateSelf();
343    }
344
345    @Override
346    public int getAlpha() {
347        if (mPaint == null) {
348            // Fast common case -- normal alpha.
349            return 0xFF;
350        }
351        return getPaint().getAlpha();
352    }
353
354    @Override
355    public void setColorFilter(ColorFilter colorFilter) {
356        if (mPaint == null && colorFilter == null) {
357            // Fast common case -- leave at no color filter.
358            return;
359        }
360        getPaint().setColorFilter(colorFilter);
361        invalidateSelf();
362    }
363
364    @Override
365    public void setTintList(ColorStateList tint) {
366        mNinePatchState.mTint = tint;
367        mTintFilter = updateTintFilter(mTintFilter, tint, mNinePatchState.mTintMode);
368        invalidateSelf();
369    }
370
371    @Override
372    public void setTintMode(PorterDuff.Mode tintMode) {
373        mNinePatchState.mTintMode = tintMode;
374        mTintFilter = updateTintFilter(mTintFilter, mNinePatchState.mTint, tintMode);
375        invalidateSelf();
376    }
377
378    @Override
379    public void setDither(boolean dither) {
380        //noinspection PointlessBooleanExpression
381        if (mPaint == null && dither == DEFAULT_DITHER) {
382            // Fast common case -- leave at default dither.
383            return;
384        }
385
386        getPaint().setDither(dither);
387        invalidateSelf();
388    }
389
390    @Override
391    public void setAutoMirrored(boolean mirrored) {
392        mNinePatchState.mAutoMirrored = mirrored;
393    }
394
395    private boolean needsMirroring() {
396        return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
397    }
398
399    @Override
400    public boolean isAutoMirrored() {
401        return mNinePatchState.mAutoMirrored;
402    }
403
404    @Override
405    public void setFilterBitmap(boolean filter) {
406        getPaint().setFilterBitmap(filter);
407        invalidateSelf();
408    }
409
410    @Override
411    public boolean isFilterBitmap() {
412        if (mPaint == null) {
413            return false;
414        }
415        return getPaint().isFilterBitmap();
416    }
417
418    @Override
419    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
420            throws XmlPullParserException, IOException {
421        super.inflate(r, parser, attrs, theme);
422
423        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.NinePatchDrawable);
424        updateStateFromTypedArray(a);
425        a.recycle();
426
427        updateLocalState(r);
428    }
429
430    /**
431     * Updates the constant state from the values in the typed array.
432     */
433    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
434        final Resources r = a.getResources();
435        final NinePatchState state = mNinePatchState;
436
437        // Account for any configuration changes.
438        state.mChangingConfigurations |= a.getChangingConfigurations();
439
440        // Extract the theme attributes, if any.
441        state.mThemeAttrs = a.extractThemeAttrs();
442
443        state.mDither = a.getBoolean(R.styleable.NinePatchDrawable_dither, state.mDither);
444
445        final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
446        if (srcResId != 0) {
447            final BitmapFactory.Options options = new BitmapFactory.Options();
448            options.inDither = !state.mDither;
449            options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
450
451            final Rect padding = new Rect();
452            final Rect opticalInsets = new Rect();
453            Bitmap bitmap = null;
454
455            try {
456                final TypedValue value = new TypedValue();
457                final InputStream is = r.openRawResource(srcResId, value);
458
459                bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
460
461                is.close();
462            } catch (IOException e) {
463                // Ignore
464            }
465
466            if (bitmap == null) {
467                throw new XmlPullParserException(a.getPositionDescription() +
468                        ": <nine-patch> requires a valid src attribute");
469            } else if (bitmap.getNinePatchChunk() == null) {
470                throw new XmlPullParserException(a.getPositionDescription() +
471                        ": <nine-patch> requires a valid 9-patch source image");
472            }
473
474            bitmap.getOpticalInsets(opticalInsets);
475
476            state.mNinePatch = new NinePatch(bitmap, bitmap.getNinePatchChunk());
477            state.mPadding = padding;
478            state.mOpticalInsets = Insets.of(opticalInsets);
479        }
480
481        state.mAutoMirrored = a.getBoolean(
482                R.styleable.NinePatchDrawable_autoMirrored, state.mAutoMirrored);
483        state.mBaseAlpha = a.getFloat(R.styleable.NinePatchDrawable_alpha, state.mBaseAlpha);
484
485        final int tintMode = a.getInt(R.styleable.NinePatchDrawable_tintMode, -1);
486        if (tintMode != -1) {
487            state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
488        }
489
490        final ColorStateList tint = a.getColorStateList(R.styleable.NinePatchDrawable_tint);
491        if (tint != null) {
492            state.mTint = tint;
493        }
494
495        state.mTargetDensity = Drawable.resolveDensity(r, state.mTargetDensity);
496    }
497
498    @Override
499    public void applyTheme(Theme t) {
500        super.applyTheme(t);
501
502        final NinePatchState state = mNinePatchState;
503        if (state == null) {
504            return;
505        }
506
507        if (state.mThemeAttrs != null) {
508            final TypedArray a = t.resolveAttributes(
509                    state.mThemeAttrs, R.styleable.NinePatchDrawable);
510            try {
511                updateStateFromTypedArray(a);
512            } catch (XmlPullParserException e) {
513                rethrowAsRuntimeException(e);
514            } finally {
515                a.recycle();
516            }
517        }
518
519        if (state.mTint != null && state.mTint.canApplyTheme()) {
520            state.mTint = state.mTint.obtainForTheme(t);
521        }
522
523        updateLocalState(t.getResources());
524    }
525
526    @Override
527    public boolean canApplyTheme() {
528        return mNinePatchState != null && mNinePatchState.canApplyTheme();
529    }
530
531    public Paint getPaint() {
532        if (mPaint == null) {
533            mPaint = new Paint();
534            mPaint.setDither(DEFAULT_DITHER);
535        }
536        return mPaint;
537    }
538
539    /**
540     * Retrieves the width of the source .png file (before resizing).
541     */
542    @Override
543    public int getIntrinsicWidth() {
544        return mBitmapWidth;
545    }
546
547    /**
548     * Retrieves the height of the source .png file (before resizing).
549     */
550    @Override
551    public int getIntrinsicHeight() {
552        return mBitmapHeight;
553    }
554
555    @Override
556    public int getMinimumWidth() {
557        return mBitmapWidth;
558    }
559
560    @Override
561    public int getMinimumHeight() {
562        return mBitmapHeight;
563    }
564
565    /**
566     * Returns a {@link android.graphics.PixelFormat graphics.PixelFormat}
567     * value of OPAQUE or TRANSLUCENT.
568     */
569    @Override
570    public int getOpacity() {
571        return mNinePatch.hasAlpha() || (mPaint != null && mPaint.getAlpha() < 255) ?
572                PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
573    }
574
575    @Override
576    public Region getTransparentRegion() {
577        return mNinePatch.getTransparentRegion(getBounds());
578    }
579
580    @Override
581    public ConstantState getConstantState() {
582        mNinePatchState.mChangingConfigurations = getChangingConfigurations();
583        return mNinePatchState;
584    }
585
586    @Override
587    public Drawable mutate() {
588        if (!mMutated && super.mutate() == this) {
589            mNinePatchState = new NinePatchState(mNinePatchState);
590            mNinePatch = mNinePatchState.mNinePatch;
591            mMutated = true;
592        }
593        return this;
594    }
595
596    /**
597     * @hide
598     */
599    public void clearMutated() {
600        super.clearMutated();
601        mMutated = false;
602    }
603
604    @Override
605    protected boolean onStateChange(int[] stateSet) {
606        final NinePatchState state = mNinePatchState;
607        if (state.mTint != null && state.mTintMode != null) {
608            mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
609            return true;
610        }
611
612        return false;
613    }
614
615    @Override
616    public boolean isStateful() {
617        final NinePatchState s = mNinePatchState;
618        return super.isStateful() || (s.mTint != null && s.mTint.isStateful());
619    }
620
621    final static class NinePatchState extends ConstantState {
622        // Values loaded during inflation.
623        int[] mThemeAttrs = null;
624        NinePatch mNinePatch = null;
625        ColorStateList mTint = null;
626        Mode mTintMode = DEFAULT_TINT_MODE;
627        Rect mPadding = null;
628        Insets mOpticalInsets = Insets.NONE;
629        float mBaseAlpha = 1.0f;
630        boolean mDither = DEFAULT_DITHER;
631        int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
632        boolean mAutoMirrored = false;
633
634        int mChangingConfigurations;
635
636        NinePatchState() {
637            // Empty constructor.
638        }
639
640        NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding) {
641            this(ninePatch, padding, null, DEFAULT_DITHER, false);
642        }
643
644        NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding,
645                @Nullable Rect opticalInsets) {
646            this(ninePatch, padding, opticalInsets, DEFAULT_DITHER, false);
647        }
648
649        NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding,
650                @Nullable Rect opticalInsets, boolean dither, boolean autoMirror) {
651            mNinePatch = ninePatch;
652            mPadding = padding;
653            mOpticalInsets = Insets.of(opticalInsets);
654            mDither = dither;
655            mAutoMirrored = autoMirror;
656        }
657
658        // Copy constructor
659
660        NinePatchState(@NonNull NinePatchState state) {
661            // We don't deep-copy any fields because they are all immutable.
662            mNinePatch = state.mNinePatch;
663            mTint = state.mTint;
664            mTintMode = state.mTintMode;
665            mThemeAttrs = state.mThemeAttrs;
666            mPadding = state.mPadding;
667            mOpticalInsets = state.mOpticalInsets;
668            mBaseAlpha = state.mBaseAlpha;
669            mDither = state.mDither;
670            mChangingConfigurations = state.mChangingConfigurations;
671            mTargetDensity = state.mTargetDensity;
672            mAutoMirrored = state.mAutoMirrored;
673        }
674
675        @Override
676        public boolean canApplyTheme() {
677            return mThemeAttrs != null
678                    || (mTint != null && mTint.canApplyTheme());
679        }
680
681        @Override
682        public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
683            final Bitmap bitmap = mNinePatch.getBitmap();
684            if (isAtlasable(bitmap) && atlasList.add(bitmap)) {
685                return bitmap.getWidth() * bitmap.getHeight();
686            }
687            return 0;
688        }
689
690        @Override
691        public Drawable newDrawable() {
692            return new NinePatchDrawable(this, null);
693        }
694
695        @Override
696        public Drawable newDrawable(Resources res) {
697            return new NinePatchDrawable(this, res);
698        }
699
700        @Override
701        public int getChangingConfigurations() {
702            return mChangingConfigurations
703                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
704        }
705    }
706
707    /**
708     * The one constructor to rule them all. This is called by all public
709     * constructors to set the state and initialize local properties.
710     */
711    private NinePatchDrawable(NinePatchState state, Resources res) {
712        mNinePatchState = state;
713
714        updateLocalState(res);
715
716        // Push density applied by setNinePatchState into state.
717        mNinePatchState.mTargetDensity = mTargetDensity;
718    }
719
720    /**
721     * Initializes local dynamic properties from state.
722     */
723    private void updateLocalState(Resources res) {
724        final NinePatchState state = mNinePatchState;
725
726        if (res != null) {
727            final int densityDpi = res.getDisplayMetrics().densityDpi;
728            mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
729        } else {
730            mTargetDensity = state.mTargetDensity;
731        }
732
733
734        // If we can, avoid calling any methods that initialize Paint.
735        if (state.mDither != DEFAULT_DITHER) {
736            setDither(state.mDither);
737        }
738
739        // Make a local copy of the padding.
740        if (state.mPadding != null) {
741            mPadding = new Rect(state.mPadding);
742        }
743
744        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
745        setNinePatch(state.mNinePatch);
746    }
747}
748