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