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