Drawable.java revision ac85f90466dd60d2af8ffc3942d503a0de606726
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.ColorInt; 20import android.annotation.NonNull; 21import android.annotation.Nullable; 22import android.content.pm.ActivityInfo.Config; 23import android.content.res.ColorStateList; 24import android.content.res.Resources; 25import android.content.res.Resources.Theme; 26import android.content.res.TypedArray; 27import android.graphics.Bitmap; 28import android.graphics.BitmapFactory; 29import android.graphics.Canvas; 30import android.graphics.Color; 31import android.graphics.ColorFilter; 32import android.graphics.Insets; 33import android.graphics.NinePatch; 34import android.graphics.Outline; 35import android.graphics.PixelFormat; 36import android.graphics.PorterDuff; 37import android.graphics.PorterDuff.Mode; 38import android.graphics.PorterDuffColorFilter; 39import android.graphics.Rect; 40import android.graphics.Region; 41import android.graphics.Xfermode; 42import android.os.Trace; 43import android.util.AttributeSet; 44import android.util.DisplayMetrics; 45import android.util.StateSet; 46import android.util.TypedValue; 47import android.util.Xml; 48import android.view.View; 49 50import org.xmlpull.v1.XmlPullParser; 51import org.xmlpull.v1.XmlPullParserException; 52 53import java.io.IOException; 54import java.io.InputStream; 55import java.lang.ref.WeakReference; 56import java.util.Arrays; 57import java.util.Collection; 58 59import com.android.internal.R; 60 61/** 62 * A Drawable is a general abstraction for "something that can be drawn." Most 63 * often you will deal with Drawable as the type of resource retrieved for 64 * drawing things to the screen; the Drawable class provides a generic API for 65 * dealing with an underlying visual resource that may take a variety of forms. 66 * Unlike a {@link android.view.View}, a Drawable does not have any facility to 67 * receive events or otherwise interact with the user. 68 * 69 * <p>In addition to simple drawing, Drawable provides a number of generic 70 * mechanisms for its client to interact with what is being drawn: 71 * 72 * <ul> 73 * <li> The {@link #setBounds} method <var>must</var> be called to tell the 74 * Drawable where it is drawn and how large it should be. All Drawables 75 * should respect the requested size, often simply by scaling their 76 * imagery. A client can find the preferred size for some Drawables with 77 * the {@link #getIntrinsicHeight} and {@link #getIntrinsicWidth} methods. 78 * 79 * <li> The {@link #getPadding} method can return from some Drawables 80 * information about how to frame content that is placed inside of them. 81 * For example, a Drawable that is intended to be the frame for a button 82 * widget would need to return padding that correctly places the label 83 * inside of itself. 84 * 85 * <li> The {@link #setState} method allows the client to tell the Drawable 86 * in which state it is to be drawn, such as "focused", "selected", etc. 87 * Some drawables may modify their imagery based on the selected state. 88 * 89 * <li> The {@link #setLevel} method allows the client to supply a single 90 * continuous controller that can modify the Drawable is displayed, such as 91 * a battery level or progress level. Some drawables may modify their 92 * imagery based on the current level. 93 * 94 * <li> A Drawable can perform animations by calling back to its client 95 * through the {@link Callback} interface. All clients should support this 96 * interface (via {@link #setCallback}) so that animations will work. A 97 * simple way to do this is through the system facilities such as 98 * {@link android.view.View#setBackground(Drawable)} and 99 * {@link android.widget.ImageView}. 100 * </ul> 101 * 102 * Though usually not visible to the application, Drawables may take a variety 103 * of forms: 104 * 105 * <ul> 106 * <li> <b>Bitmap</b>: the simplest Drawable, a PNG or JPEG image. 107 * <li> <b>Nine Patch</b>: an extension to the PNG format allows it to 108 * specify information about how to stretch it and place things inside of 109 * it. 110 * <li> <b>Shape</b>: contains simple drawing commands instead of a raw 111 * bitmap, allowing it to resize better in some cases. 112 * <li> <b>Layers</b>: a compound drawable, which draws multiple underlying 113 * drawables on top of each other. 114 * <li> <b>States</b>: a compound drawable that selects one of a set of 115 * drawables based on its state. 116 * <li> <b>Levels</b>: a compound drawable that selects one of a set of 117 * drawables based on its level. 118 * <li> <b>Scale</b>: a compound drawable with a single child drawable, 119 * whose overall size is modified based on the current level. 120 * </ul> 121 * 122 * <div class="special reference"> 123 * <h3>Developer Guides</h3> 124 * <p>For more information about how to use drawables, read the 125 * <a href="{@docRoot}guide/topics/graphics/2d-graphics.html">Canvas and Drawables</a> developer 126 * guide. For information and examples of creating drawable resources (XML or bitmap files that 127 * can be loaded in code), read the 128 * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a> 129 * document.</p></div> 130 */ 131public abstract class Drawable { 132 private static final Rect ZERO_BOUNDS_RECT = new Rect(); 133 134 static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN; 135 136 private int[] mStateSet = StateSet.WILD_CARD; 137 private int mLevel = 0; 138 private @Config int mChangingConfigurations = 0; 139 private Rect mBounds = ZERO_BOUNDS_RECT; // lazily becomes a new Rect() 140 private WeakReference<Callback> mCallback = null; 141 private boolean mVisible = true; 142 143 private int mLayoutDirection; 144 145 /** 146 * Draw in its bounds (set via setBounds) respecting optional effects such 147 * as alpha (set via setAlpha) and color filter (set via setColorFilter). 148 * 149 * @param canvas The canvas to draw into 150 */ 151 public abstract void draw(Canvas canvas); 152 153 /** 154 * Specify a bounding rectangle for the Drawable. This is where the drawable 155 * will draw when its draw() method is called. 156 */ 157 public void setBounds(int left, int top, int right, int bottom) { 158 Rect oldBounds = mBounds; 159 160 if (oldBounds == ZERO_BOUNDS_RECT) { 161 oldBounds = mBounds = new Rect(); 162 } 163 164 if (oldBounds.left != left || oldBounds.top != top || 165 oldBounds.right != right || oldBounds.bottom != bottom) { 166 if (!oldBounds.isEmpty()) { 167 // first invalidate the previous bounds 168 invalidateSelf(); 169 } 170 mBounds.set(left, top, right, bottom); 171 onBoundsChange(mBounds); 172 } 173 } 174 175 /** 176 * Specify a bounding rectangle for the Drawable. This is where the drawable 177 * will draw when its draw() method is called. 178 */ 179 public void setBounds(Rect bounds) { 180 setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom); 181 } 182 183 /** 184 * Return a copy of the drawable's bounds in the specified Rect (allocated 185 * by the caller). The bounds specify where this will draw when its draw() 186 * method is called. 187 * 188 * @param bounds Rect to receive the drawable's bounds (allocated by the 189 * caller). 190 */ 191 public final void copyBounds(Rect bounds) { 192 bounds.set(mBounds); 193 } 194 195 /** 196 * Return a copy of the drawable's bounds in a new Rect. This returns the 197 * same values as getBounds(), but the returned object is guaranteed to not 198 * be changed later by the drawable (i.e. it retains no reference to this 199 * rect). If the caller already has a Rect allocated, call copyBounds(rect). 200 * 201 * @return A copy of the drawable's bounds 202 */ 203 public final Rect copyBounds() { 204 return new Rect(mBounds); 205 } 206 207 /** 208 * Return the drawable's bounds Rect. Note: for efficiency, the returned 209 * object may be the same object stored in the drawable (though this is not 210 * guaranteed), so if a persistent copy of the bounds is needed, call 211 * copyBounds(rect) instead. 212 * You should also not change the object returned by this method as it may 213 * be the same object stored in the drawable. 214 * 215 * @return The bounds of the drawable (which may change later, so caller 216 * beware). DO NOT ALTER the returned object as it may change the 217 * stored bounds of this drawable. 218 * 219 * @see #copyBounds() 220 * @see #copyBounds(android.graphics.Rect) 221 */ 222 public final Rect getBounds() { 223 if (mBounds == ZERO_BOUNDS_RECT) { 224 mBounds = new Rect(); 225 } 226 227 return mBounds; 228 } 229 230 /** 231 * Return the drawable's dirty bounds Rect. Note: for efficiency, the 232 * returned object may be the same object stored in the drawable (though 233 * this is not guaranteed). 234 * <p> 235 * By default, this returns the full drawable bounds. Custom drawables may 236 * override this method to perform more precise invalidation. 237 * 238 * @return The dirty bounds of this drawable 239 */ 240 public Rect getDirtyBounds() { 241 return getBounds(); 242 } 243 244 /** 245 * Set a mask of the configuration parameters for which this drawable 246 * may change, requiring that it be re-created. 247 * 248 * @param configs A mask of the changing configuration parameters, as 249 * defined by {@link android.content.pm.ActivityInfo}. 250 * 251 * @see android.content.pm.ActivityInfo 252 */ 253 public void setChangingConfigurations(@Config int configs) { 254 mChangingConfigurations = configs; 255 } 256 257 /** 258 * Return a mask of the configuration parameters for which this drawable 259 * may change, requiring that it be re-created. The default implementation 260 * returns whatever was provided through 261 * {@link #setChangingConfigurations(int)} or 0 by default. Subclasses 262 * may extend this to or in the changing configurations of any other 263 * drawables they hold. 264 * 265 * @return Returns a mask of the changing configuration parameters, as 266 * defined by {@link android.content.pm.ActivityInfo}. 267 * 268 * @see android.content.pm.ActivityInfo 269 */ 270 public @Config int getChangingConfigurations() { 271 return mChangingConfigurations; 272 } 273 274 /** 275 * Set to true to have the drawable dither its colors when drawn to a 276 * device with fewer than 8-bits per color component. 277 * 278 * @see android.graphics.Paint#setDither(boolean); 279 * @deprecated This property is ignored. 280 */ 281 @Deprecated 282 public void setDither(boolean dither) {} 283 284 /** 285 * Set to true to have the drawable filter its bitmaps with bilinear 286 * sampling when they are scaled or rotated. 287 * 288 * <p>This can improve appearance when bitmaps are rotated. If the drawable 289 * does not use bitmaps, this call is ignored.</p> 290 * 291 * @see #isFilterBitmap() 292 * @see android.graphics.Paint#setFilterBitmap(boolean); 293 */ 294 public void setFilterBitmap(boolean filter) {} 295 296 /** 297 * @return whether this drawable filters its bitmaps 298 * @see #setFilterBitmap(boolean) 299 */ 300 public boolean isFilterBitmap() { 301 return false; 302 } 303 304 /** 305 * Implement this interface if you want to create an animated drawable that 306 * extends {@link android.graphics.drawable.Drawable Drawable}. 307 * Upon retrieving a drawable, use 308 * {@link Drawable#setCallback(android.graphics.drawable.Drawable.Callback)} 309 * to supply your implementation of the interface to the drawable; it uses 310 * this interface to schedule and execute animation changes. 311 */ 312 public static interface Callback { 313 /** 314 * Called when the drawable needs to be redrawn. A view at this point 315 * should invalidate itself (or at least the part of itself where the 316 * drawable appears). 317 * 318 * @param who The drawable that is requesting the update. 319 */ 320 public void invalidateDrawable(Drawable who); 321 322 /** 323 * A Drawable can call this to schedule the next frame of its 324 * animation. An implementation can generally simply call 325 * {@link android.os.Handler#postAtTime(Runnable, Object, long)} with 326 * the parameters <var>(what, who, when)</var> to perform the 327 * scheduling. 328 * 329 * @param who The drawable being scheduled. 330 * @param what The action to execute. 331 * @param when The time (in milliseconds) to run. The timebase is 332 * {@link android.os.SystemClock#uptimeMillis} 333 */ 334 public void scheduleDrawable(Drawable who, Runnable what, long when); 335 336 /** 337 * A Drawable can call this to unschedule an action previously 338 * scheduled with {@link #scheduleDrawable}. An implementation can 339 * generally simply call 340 * {@link android.os.Handler#removeCallbacks(Runnable, Object)} with 341 * the parameters <var>(what, who)</var> to unschedule the drawable. 342 * 343 * @param who The drawable being unscheduled. 344 * @param what The action being unscheduled. 345 */ 346 public void unscheduleDrawable(Drawable who, Runnable what); 347 } 348 349 /** 350 * Bind a {@link Callback} object to this Drawable. Required for clients 351 * that want to support animated drawables. 352 * 353 * @param cb The client's Callback implementation. 354 * 355 * @see #getCallback() 356 */ 357 public final void setCallback(Callback cb) { 358 mCallback = new WeakReference<Callback>(cb); 359 } 360 361 /** 362 * Return the current {@link Callback} implementation attached to this 363 * Drawable. 364 * 365 * @return A {@link Callback} instance or null if no callback was set. 366 * 367 * @see #setCallback(android.graphics.drawable.Drawable.Callback) 368 */ 369 public Callback getCallback() { 370 if (mCallback != null) { 371 return mCallback.get(); 372 } 373 return null; 374 } 375 376 /** 377 * Use the current {@link Callback} implementation to have this Drawable 378 * redrawn. Does nothing if there is no Callback attached to the 379 * Drawable. 380 * 381 * @see Callback#invalidateDrawable 382 * @see #getCallback() 383 * @see #setCallback(android.graphics.drawable.Drawable.Callback) 384 */ 385 public void invalidateSelf() { 386 final Callback callback = getCallback(); 387 if (callback != null) { 388 callback.invalidateDrawable(this); 389 } 390 } 391 392 /** 393 * Use the current {@link Callback} implementation to have this Drawable 394 * scheduled. Does nothing if there is no Callback attached to the 395 * Drawable. 396 * 397 * @param what The action being scheduled. 398 * @param when The time (in milliseconds) to run. 399 * 400 * @see Callback#scheduleDrawable 401 */ 402 public void scheduleSelf(Runnable what, long when) { 403 final Callback callback = getCallback(); 404 if (callback != null) { 405 callback.scheduleDrawable(this, what, when); 406 } 407 } 408 409 /** 410 * Use the current {@link Callback} implementation to have this Drawable 411 * unscheduled. Does nothing if there is no Callback attached to the 412 * Drawable. 413 * 414 * @param what The runnable that you no longer want called. 415 * 416 * @see Callback#unscheduleDrawable 417 */ 418 public void unscheduleSelf(Runnable what) { 419 final Callback callback = getCallback(); 420 if (callback != null) { 421 callback.unscheduleDrawable(this, what); 422 } 423 } 424 425 /** 426 * Returns the resolved layout direction for this Drawable. 427 * 428 * @return One of {@link android.view.View#LAYOUT_DIRECTION_LTR}, 429 * {@link android.view.View#LAYOUT_DIRECTION_RTL} 430 * @see #setLayoutDirection(int) 431 */ 432 public int getLayoutDirection() { 433 return mLayoutDirection; 434 } 435 436 /** 437 * Set the layout direction for this drawable. Should be a resolved 438 * layout direction, as the Drawable has no capacity to do the resolution on 439 * its own. 440 * 441 * @param layoutDirection the resolved layout direction for the drawable, 442 * either {@link android.view.View#LAYOUT_DIRECTION_LTR} 443 * or {@link android.view.View#LAYOUT_DIRECTION_RTL} 444 * @see #getLayoutDirection() 445 */ 446 public final boolean setLayoutDirection(@View.ResolvedLayoutDir int layoutDirection) { 447 if (mLayoutDirection != layoutDirection) { 448 mLayoutDirection = layoutDirection; 449 return onLayoutDirectionChanged(layoutDirection); 450 } 451 return false; 452 } 453 454 /** 455 * Called when the drawable's resolved layout direction changes. 456 * 457 * @param layoutDirection the new resolved layout direction 458 * @return true if the layout direction change has caused the appearance of 459 * the drawable to change and it needs to be re-drawn 460 * @see #setLayoutDirection(int) 461 */ 462 public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) { 463 return false; 464 } 465 466 /** 467 * Specify an alpha value for the drawable. 0 means fully transparent, and 468 * 255 means fully opaque. 469 */ 470 public abstract void setAlpha(int alpha); 471 472 /** 473 * Gets the current alpha value for the drawable. 0 means fully transparent, 474 * 255 means fully opaque. This method is implemented by 475 * Drawable subclasses and the value returned is specific to how that class treats alpha. 476 * The default return value is 255 if the class does not override this method to return a value 477 * specific to its use of alpha. 478 */ 479 public int getAlpha() { 480 return 0xFF; 481 } 482 483 /** 484 * @hide 485 * 486 * Internal-only method for setting xfermode on certain supported drawables. 487 * 488 * Should not be made public since the layers and drawing area with which 489 * Drawables draw is private implementation detail, and not something apps 490 * should rely upon. 491 */ 492 public void setXfermode(Xfermode mode) { 493 // Base implementation drops it on the floor for compatibility. Whee! 494 } 495 496 /** 497 * Specify an optional color filter for the drawable. 498 * <p> 499 * If a Drawable has a ColorFilter, each output pixel of the Drawable's 500 * drawing contents will be modified by the color filter before it is 501 * blended onto the render target of a Canvas. 502 * </p> 503 * <p> 504 * Pass {@code null} to remove any existing color filter. 505 * </p> 506 * <p class="note"><strong>Note:</strong> Setting a non-{@code null} color 507 * filter disables {@link #setTintList(ColorStateList) tint}. 508 * </p> 509 * 510 * @param colorFilter The color filter to apply, or {@code null} to remove the 511 * existing color filter 512 */ 513 public abstract void setColorFilter(@Nullable ColorFilter colorFilter); 514 515 /** 516 * Specify a color and Porter-Duff mode to be the color filter for this 517 * drawable. 518 * <p> 519 * Convenience for {@link #setColorFilter(ColorFilter)} which constructs a 520 * {@link PorterDuffColorFilter}. 521 * </p> 522 * <p class="note"><strong>Note:</strong> Setting a color filter disables 523 * {@link #setTintList(ColorStateList) tint}. 524 * </p> 525 */ 526 public void setColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) { 527 setColorFilter(new PorterDuffColorFilter(color, mode)); 528 } 529 530 /** 531 * Specifies tint color for this drawable. 532 * <p> 533 * A Drawable's drawing content will be blended together with its tint 534 * before it is drawn to the screen. This functions similarly to 535 * {@link #setColorFilter(int, PorterDuff.Mode)}. 536 * </p> 537 * <p> 538 * To clear the tint, pass {@code null} to 539 * {@link #setTintList(ColorStateList)}. 540 * </p> 541 * <p class="note"><strong>Note:</strong> Setting a color filter via 542 * {@link #setColorFilter(ColorFilter)} or 543 * {@link #setColorFilter(int, PorterDuff.Mode)} overrides tint. 544 * </p> 545 * 546 * @param tintColor Color to use for tinting this drawable 547 * @see #setTintList(ColorStateList) 548 * @see #setTintMode(PorterDuff.Mode) 549 */ 550 public void setTint(@ColorInt int tintColor) { 551 setTintList(ColorStateList.valueOf(tintColor)); 552 } 553 554 /** 555 * Specifies tint color for this drawable as a color state list. 556 * <p> 557 * A Drawable's drawing content will be blended together with its tint 558 * before it is drawn to the screen. This functions similarly to 559 * {@link #setColorFilter(int, PorterDuff.Mode)}. 560 * </p> 561 * <p class="note"><strong>Note:</strong> Setting a color filter via 562 * {@link #setColorFilter(ColorFilter)} or 563 * {@link #setColorFilter(int, PorterDuff.Mode)} overrides tint. 564 * </p> 565 * 566 * @param tint Color state list to use for tinting this drawable, or 567 * {@code null} to clear the tint 568 * @see #setTint(int) 569 * @see #setTintMode(PorterDuff.Mode) 570 */ 571 public void setTintList(@Nullable ColorStateList tint) {} 572 573 /** 574 * Specifies a tint blending mode for this drawable. 575 * <p> 576 * Defines how this drawable's tint color should be blended into the drawable 577 * before it is drawn to screen. Default tint mode is {@link PorterDuff.Mode#SRC_IN}. 578 * </p> 579 * <p class="note"><strong>Note:</strong> Setting a color filter via 580 * {@link #setColorFilter(ColorFilter)} or 581 * {@link #setColorFilter(int, PorterDuff.Mode)} overrides tint. 582 * </p> 583 * 584 * @param tintMode A Porter-Duff blending mode 585 * @see #setTint(int) 586 * @see #setTintList(ColorStateList) 587 */ 588 public void setTintMode(@NonNull PorterDuff.Mode tintMode) {} 589 590 /** 591 * Returns the current color filter, or {@code null} if none set. 592 * 593 * @return the current color filter, or {@code null} if none set 594 */ 595 public ColorFilter getColorFilter() { 596 return null; 597 } 598 599 /** 600 * Removes the color filter for this drawable. 601 */ 602 public void clearColorFilter() { 603 setColorFilter(null); 604 } 605 606 /** 607 * Specifies the hotspot's location within the drawable. 608 * 609 * @param x The X coordinate of the center of the hotspot 610 * @param y The Y coordinate of the center of the hotspot 611 */ 612 public void setHotspot(float x, float y) {} 613 614 /** 615 * Sets the bounds to which the hotspot is constrained, if they should be 616 * different from the drawable bounds. 617 * 618 * @param left position in pixels of the left bound 619 * @param top position in pixels of the top bound 620 * @param right position in pixels of the right bound 621 * @param bottom position in pixels of the bottom bound 622 * @see #getHotspotBounds(android.graphics.Rect) 623 */ 624 public void setHotspotBounds(int left, int top, int right, int bottom) {} 625 626 /** 627 * Populates {@code outRect} with the hotspot bounds. 628 * 629 * @param outRect the rect to populate with the hotspot bounds 630 * @see #setHotspotBounds(int, int, int, int) 631 */ 632 public void getHotspotBounds(Rect outRect) { 633 outRect.set(getBounds()); 634 } 635 636 /** 637 * Whether this drawable requests projection. 638 * 639 * @hide magic! 640 */ 641 public boolean isProjected() { 642 return false; 643 } 644 645 /** 646 * Indicates whether this drawable will change its appearance based on 647 * state. Clients can use this to determine whether it is necessary to 648 * calculate their state and call setState. 649 * 650 * @return True if this drawable changes its appearance based on state, 651 * false otherwise. 652 * @see #setState(int[]) 653 */ 654 public boolean isStateful() { 655 return false; 656 } 657 658 /** 659 * Specify a set of states for the drawable. These are use-case specific, 660 * so see the relevant documentation. As an example, the background for 661 * widgets like Button understand the following states: 662 * [{@link android.R.attr#state_focused}, 663 * {@link android.R.attr#state_pressed}]. 664 * 665 * <p>If the new state you are supplying causes the appearance of the 666 * Drawable to change, then it is responsible for calling 667 * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em> 668 * true will be returned from this function. 669 * 670 * <p>Note: The Drawable holds a reference on to <var>stateSet</var> 671 * until a new state array is given to it, so you must not modify this 672 * array during that time.</p> 673 * 674 * @param stateSet The new set of states to be displayed. 675 * 676 * @return Returns true if this change in state has caused the appearance 677 * of the Drawable to change (hence requiring an invalidate), otherwise 678 * returns false. 679 */ 680 public boolean setState(final int[] stateSet) { 681 if (!Arrays.equals(mStateSet, stateSet)) { 682 mStateSet = stateSet; 683 return onStateChange(stateSet); 684 } 685 return false; 686 } 687 688 /** 689 * Describes the current state, as a union of primitve states, such as 690 * {@link android.R.attr#state_focused}, 691 * {@link android.R.attr#state_selected}, etc. 692 * Some drawables may modify their imagery based on the selected state. 693 * @return An array of resource Ids describing the current state. 694 */ 695 public int[] getState() { 696 return mStateSet; 697 } 698 699 /** 700 * If this Drawable does transition animations between states, ask that 701 * it immediately jump to the current state and skip any active animations. 702 */ 703 public void jumpToCurrentState() { 704 } 705 706 /** 707 * @return The current drawable that will be used by this drawable. For simple drawables, this 708 * is just the drawable itself. For drawables that change state like 709 * {@link StateListDrawable} and {@link LevelListDrawable} this will be the child drawable 710 * currently in use. 711 */ 712 public Drawable getCurrent() { 713 return this; 714 } 715 716 /** 717 * Specify the level for the drawable. This allows a drawable to vary its 718 * imagery based on a continuous controller, for example to show progress 719 * or volume level. 720 * 721 * <p>If the new level you are supplying causes the appearance of the 722 * Drawable to change, then it is responsible for calling 723 * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em> 724 * true will be returned from this function. 725 * 726 * @param level The new level, from 0 (minimum) to 10000 (maximum). 727 * 728 * @return Returns true if this change in level has caused the appearance 729 * of the Drawable to change (hence requiring an invalidate), otherwise 730 * returns false. 731 */ 732 public final boolean setLevel(int level) { 733 if (mLevel != level) { 734 mLevel = level; 735 return onLevelChange(level); 736 } 737 return false; 738 } 739 740 /** 741 * Retrieve the current level. 742 * 743 * @return int Current level, from 0 (minimum) to 10000 (maximum). 744 */ 745 public final int getLevel() { 746 return mLevel; 747 } 748 749 /** 750 * Set whether this Drawable is visible. This generally does not impact 751 * the Drawable's behavior, but is a hint that can be used by some 752 * Drawables, for example, to decide whether run animations. 753 * 754 * @param visible Set to true if visible, false if not. 755 * @param restart You can supply true here to force the drawable to behave 756 * as if it has just become visible, even if it had last 757 * been set visible. Used for example to force animations 758 * to restart. 759 * 760 * @return boolean Returns true if the new visibility is different than 761 * its previous state. 762 */ 763 public boolean setVisible(boolean visible, boolean restart) { 764 boolean changed = mVisible != visible; 765 if (changed) { 766 mVisible = visible; 767 invalidateSelf(); 768 } 769 return changed; 770 } 771 772 public final boolean isVisible() { 773 return mVisible; 774 } 775 776 /** 777 * Set whether this Drawable is automatically mirrored when its layout direction is RTL 778 * (right-to left). See {@link android.util.LayoutDirection}. 779 * 780 * @param mirrored Set to true if the Drawable should be mirrored, false if not. 781 */ 782 public void setAutoMirrored(boolean mirrored) { 783 } 784 785 /** 786 * Tells if this Drawable will be automatically mirrored when its layout direction is RTL 787 * right-to-left. See {@link android.util.LayoutDirection}. 788 * 789 * @return boolean Returns true if this Drawable will be automatically mirrored. 790 */ 791 public boolean isAutoMirrored() { 792 return false; 793 } 794 795 /** 796 * Applies the specified theme to this Drawable and its children. 797 * 798 * @param t the theme to apply 799 */ 800 public void applyTheme(@NonNull @SuppressWarnings("unused") Theme t) { 801 } 802 803 public boolean canApplyTheme() { 804 return false; 805 } 806 807 /** 808 * Return the opacity/transparency of this Drawable. The returned value is 809 * one of the abstract format constants in 810 * {@link android.graphics.PixelFormat}: 811 * {@link android.graphics.PixelFormat#UNKNOWN}, 812 * {@link android.graphics.PixelFormat#TRANSLUCENT}, 813 * {@link android.graphics.PixelFormat#TRANSPARENT}, or 814 * {@link android.graphics.PixelFormat#OPAQUE}. 815 * 816 * <p>An OPAQUE drawable is one that draws all all content within its bounds, completely 817 * covering anything behind the drawable. A TRANSPARENT drawable is one that draws nothing 818 * within its bounds, allowing everything behind it to show through. A TRANSLUCENT drawable 819 * is a drawable in any other state, where the drawable will draw some, but not all, 820 * of the content within its bounds and at least some content behind the drawable will 821 * be visible. If the visibility of the drawable's contents cannot be determined, the 822 * safest/best return value is TRANSLUCENT. 823 * 824 * <p>Generally a Drawable should be as conservative as possible with the 825 * value it returns. For example, if it contains multiple child drawables 826 * and only shows one of them at a time, if only one of the children is 827 * TRANSLUCENT and the others are OPAQUE then TRANSLUCENT should be 828 * returned. You can use the method {@link #resolveOpacity} to perform a 829 * standard reduction of two opacities to the appropriate single output. 830 * 831 * <p>Note that the returned value does not necessarily take into account a 832 * custom alpha or color filter that has been applied by the client through 833 * the {@link #setAlpha} or {@link #setColorFilter} methods. Some subclasses, 834 * such as {@link BitmapDrawable}, {@link ColorDrawable}, and {@link GradientDrawable}, 835 * do account for the value of {@link #setAlpha}, but the general behavior is dependent 836 * upon the implementation of the subclass. 837 * 838 * @return int The opacity class of the Drawable. 839 * 840 * @see android.graphics.PixelFormat 841 */ 842 public abstract int getOpacity(); 843 844 /** 845 * Return the appropriate opacity value for two source opacities. If 846 * either is UNKNOWN, that is returned; else, if either is TRANSLUCENT, 847 * that is returned; else, if either is TRANSPARENT, that is returned; 848 * else, OPAQUE is returned. 849 * 850 * <p>This is to help in implementing {@link #getOpacity}. 851 * 852 * @param op1 One opacity value. 853 * @param op2 Another opacity value. 854 * 855 * @return int The combined opacity value. 856 * 857 * @see #getOpacity 858 */ 859 public static int resolveOpacity(int op1, int op2) { 860 if (op1 == op2) { 861 return op1; 862 } 863 if (op1 == PixelFormat.UNKNOWN || op2 == PixelFormat.UNKNOWN) { 864 return PixelFormat.UNKNOWN; 865 } 866 if (op1 == PixelFormat.TRANSLUCENT || op2 == PixelFormat.TRANSLUCENT) { 867 return PixelFormat.TRANSLUCENT; 868 } 869 if (op1 == PixelFormat.TRANSPARENT || op2 == PixelFormat.TRANSPARENT) { 870 return PixelFormat.TRANSPARENT; 871 } 872 return PixelFormat.OPAQUE; 873 } 874 875 /** 876 * Returns a Region representing the part of the Drawable that is completely 877 * transparent. This can be used to perform drawing operations, identifying 878 * which parts of the target will not change when rendering the Drawable. 879 * The default implementation returns null, indicating no transparent 880 * region; subclasses can optionally override this to return an actual 881 * Region if they want to supply this optimization information, but it is 882 * not required that they do so. 883 * 884 * @return Returns null if the Drawables has no transparent region to 885 * report, else a Region holding the parts of the Drawable's bounds that 886 * are transparent. 887 */ 888 public Region getTransparentRegion() { 889 return null; 890 } 891 892 /** 893 * Override this in your subclass to change appearance if you recognize the 894 * specified state. 895 * 896 * @return Returns true if the state change has caused the appearance of 897 * the Drawable to change (that is, it needs to be drawn), else false 898 * if it looks the same and there is no need to redraw it since its 899 * last state. 900 */ 901 protected boolean onStateChange(int[] state) { return false; } 902 /** Override this in your subclass to change appearance if you vary based 903 * on level. 904 * @return Returns true if the level change has caused the appearance of 905 * the Drawable to change (that is, it needs to be drawn), else false 906 * if it looks the same and there is no need to redraw it since its 907 * last level. 908 */ 909 protected boolean onLevelChange(int level) { return false; } 910 /** 911 * Override this in your subclass to change appearance if you vary based on 912 * the bounds. 913 */ 914 protected void onBoundsChange(Rect bounds) {} 915 916 /** 917 * Returns the drawable's intrinsic width. 918 * <p> 919 * Intrinsic width is the width at which the drawable would like to be laid 920 * out, including any inherent padding. If the drawable has no intrinsic 921 * width, such as a solid color, this method returns -1. 922 * 923 * @return the intrinsic width, or -1 if no intrinsic width 924 */ 925 public int getIntrinsicWidth() { 926 return -1; 927 } 928 929 /** 930 * Returns the drawable's intrinsic height. 931 * <p> 932 * Intrinsic height is the height at which the drawable would like to be 933 * laid out, including any inherent padding. If the drawable has no 934 * intrinsic height, such as a solid color, this method returns -1. 935 * 936 * @return the intrinsic height, or -1 if no intrinsic height 937 */ 938 public int getIntrinsicHeight() { 939 return -1; 940 } 941 942 /** 943 * Returns the minimum width suggested by this Drawable. If a View uses this 944 * Drawable as a background, it is suggested that the View use at least this 945 * value for its width. (There will be some scenarios where this will not be 946 * possible.) This value should INCLUDE any padding. 947 * 948 * @return The minimum width suggested by this Drawable. If this Drawable 949 * doesn't have a suggested minimum width, 0 is returned. 950 */ 951 public int getMinimumWidth() { 952 final int intrinsicWidth = getIntrinsicWidth(); 953 return intrinsicWidth > 0 ? intrinsicWidth : 0; 954 } 955 956 /** 957 * Returns the minimum height suggested by this Drawable. If a View uses this 958 * Drawable as a background, it is suggested that the View use at least this 959 * value for its height. (There will be some scenarios where this will not be 960 * possible.) This value should INCLUDE any padding. 961 * 962 * @return The minimum height suggested by this Drawable. If this Drawable 963 * doesn't have a suggested minimum height, 0 is returned. 964 */ 965 public int getMinimumHeight() { 966 final int intrinsicHeight = getIntrinsicHeight(); 967 return intrinsicHeight > 0 ? intrinsicHeight : 0; 968 } 969 970 /** 971 * Return in padding the insets suggested by this Drawable for placing 972 * content inside the drawable's bounds. Positive values move toward the 973 * center of the Drawable (set Rect.inset). 974 * 975 * @return true if this drawable actually has a padding, else false. When false is returned, 976 * the padding is always set to 0. 977 */ 978 public boolean getPadding(@NonNull Rect padding) { 979 padding.set(0, 0, 0, 0); 980 return false; 981 } 982 983 /** 984 * Return in insets the layout insets suggested by this Drawable for use with alignment 985 * operations during layout. 986 * 987 * @hide 988 */ 989 public Insets getOpticalInsets() { 990 return Insets.NONE; 991 } 992 993 /** 994 * Called to get the drawable to populate the Outline that defines its drawing area. 995 * <p> 996 * This method is called by the default {@link android.view.ViewOutlineProvider} to define 997 * the outline of the View. 998 * <p> 999 * The default behavior defines the outline to be the bounding rectangle of 0 alpha. 1000 * Subclasses that wish to convey a different shape or alpha value must override this method. 1001 * 1002 * @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider) 1003 */ 1004 public void getOutline(@NonNull Outline outline) { 1005 outline.setRect(getBounds()); 1006 outline.setAlpha(0); 1007 } 1008 1009 /** 1010 * Make this drawable mutable. This operation cannot be reversed. A mutable 1011 * drawable is guaranteed to not share its state with any other drawable. 1012 * This is especially useful when you need to modify properties of drawables 1013 * loaded from resources. By default, all drawables instances loaded from 1014 * the same resource share a common state; if you modify the state of one 1015 * instance, all the other instances will receive the same modification. 1016 * 1017 * Calling this method on a mutable Drawable will have no effect. 1018 * 1019 * @return This drawable. 1020 * @see ConstantState 1021 * @see #getConstantState() 1022 */ 1023 public Drawable mutate() { 1024 return this; 1025 } 1026 1027 /** 1028 * Clears the mutated state, allowing this drawable to be cached and 1029 * mutated again. 1030 * <p> 1031 * This is hidden because only framework drawables can be cached, so 1032 * custom drawables don't need to support constant state, mutate(), or 1033 * clearMutated(). 1034 * 1035 * @hide 1036 */ 1037 public void clearMutated() { 1038 // Default implementation is no-op. 1039 } 1040 1041 /** 1042 * Create a drawable from an inputstream 1043 */ 1044 public static Drawable createFromStream(InputStream is, String srcName) { 1045 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable"); 1046 try { 1047 return createFromResourceStream(null, null, is, srcName); 1048 } finally { 1049 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); 1050 } 1051 } 1052 1053 /** 1054 * Create a drawable from an inputstream, using the given resources and 1055 * value to determine density information. 1056 */ 1057 public static Drawable createFromResourceStream(Resources res, TypedValue value, 1058 InputStream is, String srcName) { 1059 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable"); 1060 try { 1061 return createFromResourceStream(res, value, is, srcName, null); 1062 } finally { 1063 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); 1064 } 1065 } 1066 1067 /** 1068 * Create a drawable from an inputstream, using the given resources and 1069 * value to determine density information. 1070 */ 1071 public static Drawable createFromResourceStream(Resources res, TypedValue value, 1072 InputStream is, String srcName, BitmapFactory.Options opts) { 1073 if (is == null) { 1074 return null; 1075 } 1076 1077 /* ugh. The decodeStream contract is that we have already allocated 1078 the pad rect, but if the bitmap does not had a ninepatch chunk, 1079 then the pad will be ignored. If we could change this to lazily 1080 alloc/assign the rect, we could avoid the GC churn of making new 1081 Rects only to drop them on the floor. 1082 */ 1083 Rect pad = new Rect(); 1084 1085 // Special stuff for compatibility mode: if the target density is not 1086 // the same as the display density, but the resource -is- the same as 1087 // the display density, then don't scale it down to the target density. 1088 // This allows us to load the system's density-correct resources into 1089 // an application in compatibility mode, without scaling those down 1090 // to the compatibility density only to have them scaled back up when 1091 // drawn to the screen. 1092 if (opts == null) opts = new BitmapFactory.Options(); 1093 opts.inScreenDensity = Drawable.resolveDensity(res, 0); 1094 Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts); 1095 if (bm != null) { 1096 byte[] np = bm.getNinePatchChunk(); 1097 if (np == null || !NinePatch.isNinePatchChunk(np)) { 1098 np = null; 1099 pad = null; 1100 } 1101 1102 final Rect opticalInsets = new Rect(); 1103 bm.getOpticalInsets(opticalInsets); 1104 return drawableFromBitmap(res, bm, np, pad, opticalInsets, srcName); 1105 } 1106 return null; 1107 } 1108 1109 /** 1110 * Create a drawable from an XML document. For more information on how to 1111 * create resources in XML, see 1112 * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>. 1113 */ 1114 public static Drawable createFromXml(Resources r, XmlPullParser parser) 1115 throws XmlPullParserException, IOException { 1116 return createFromXml(r, parser, null); 1117 } 1118 1119 /** 1120 * Create a drawable from an XML document using an optional {@link Theme}. 1121 * For more information on how to create resources in XML, see 1122 * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>. 1123 */ 1124 public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme) 1125 throws XmlPullParserException, IOException { 1126 AttributeSet attrs = Xml.asAttributeSet(parser); 1127 1128 int type; 1129 while ((type=parser.next()) != XmlPullParser.START_TAG && 1130 type != XmlPullParser.END_DOCUMENT) { 1131 // Empty loop 1132 } 1133 1134 if (type != XmlPullParser.START_TAG) { 1135 throw new XmlPullParserException("No start tag found"); 1136 } 1137 1138 Drawable drawable = createFromXmlInner(r, parser, attrs, theme); 1139 1140 if (drawable == null) { 1141 throw new RuntimeException("Unknown initial tag: " + parser.getName()); 1142 } 1143 1144 return drawable; 1145 } 1146 1147 /** 1148 * Create from inside an XML document. Called on a parser positioned at 1149 * a tag in an XML document, tries to create a Drawable from that tag. 1150 * Returns null if the tag is not a valid drawable. 1151 */ 1152 public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs) 1153 throws XmlPullParserException, IOException { 1154 return createFromXmlInner(r, parser, attrs, null); 1155 } 1156 1157 /** 1158 * Create a drawable from inside an XML document using an optional 1159 * {@link Theme}. Called on a parser positioned at a tag in an XML 1160 * document, tries to create a Drawable from that tag. Returns {@code null} 1161 * if the tag is not a valid drawable. 1162 */ 1163 public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs, 1164 Theme theme) throws XmlPullParserException, IOException { 1165 return r.getDrawableInflater().inflateFromXml(parser.getName(), parser, attrs, theme); 1166 } 1167 1168 /** 1169 * Create a drawable from file path name. 1170 */ 1171 public static Drawable createFromPath(String pathName) { 1172 if (pathName == null) { 1173 return null; 1174 } 1175 1176 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName); 1177 try { 1178 Bitmap bm = BitmapFactory.decodeFile(pathName); 1179 if (bm != null) { 1180 return drawableFromBitmap(null, bm, null, null, null, pathName); 1181 } 1182 } finally { 1183 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); 1184 } 1185 1186 return null; 1187 } 1188 1189 /** 1190 * Inflate this Drawable from an XML resource. Does not apply a theme. 1191 * 1192 * @see #inflate(Resources, XmlPullParser, AttributeSet, Theme) 1193 */ 1194 public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, 1195 @NonNull AttributeSet attrs) throws XmlPullParserException, IOException { 1196 inflate(r, parser, attrs, null); 1197 } 1198 1199 /** 1200 * Inflate this Drawable from an XML resource optionally styled by a theme. 1201 * 1202 * @param r Resources used to resolve attribute values 1203 * @param parser XML parser from which to inflate this Drawable 1204 * @param attrs Base set of attribute values 1205 * @param theme Theme to apply, may be null 1206 * @throws XmlPullParserException 1207 * @throws IOException 1208 */ 1209 public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, 1210 @NonNull AttributeSet attrs, @Nullable Theme theme) 1211 throws XmlPullParserException, IOException { 1212 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.Drawable); 1213 mVisible = a.getBoolean(R.styleable.Drawable_visible, mVisible); 1214 a.recycle(); 1215 } 1216 1217 /** 1218 * Inflate a Drawable from an XML resource. 1219 * 1220 * @throws XmlPullParserException 1221 * @throws IOException 1222 */ 1223 void inflateWithAttributes(@NonNull Resources r, @NonNull XmlPullParser parser, 1224 @NonNull TypedArray attrs, int visibleAttr) throws XmlPullParserException, IOException { 1225 mVisible = attrs.getBoolean(visibleAttr, mVisible); 1226 } 1227 1228 /** 1229 * This abstract class is used by {@link Drawable}s to store shared constant state and data 1230 * between Drawables. {@link BitmapDrawable}s created from the same resource will for instance 1231 * share a unique bitmap stored in their ConstantState. 1232 * 1233 * <p> 1234 * {@link #newDrawable(Resources)} can be used as a factory to create new Drawable instances 1235 * from this ConstantState. 1236 * </p> 1237 * 1238 * Use {@link Drawable#getConstantState()} to retrieve the ConstantState of a Drawable. Calling 1239 * {@link Drawable#mutate()} on a Drawable should typically create a new ConstantState for that 1240 * Drawable. 1241 */ 1242 public static abstract class ConstantState { 1243 /** 1244 * Creates a new Drawable instance from its constant state. 1245 * <p> 1246 * <strong>Note:</strong> Using this method means density-dependent 1247 * properties, such as pixel dimensions or bitmap images, will not be 1248 * updated to match the density of the target display. To ensure 1249 * correct scaling, use {@link #newDrawable(Resources)} instead to 1250 * provide an appropriate Resources object. 1251 * 1252 * @return a new drawable object based on this constant state 1253 * @see {@link #newDrawable(Resources)} 1254 */ 1255 @NonNull 1256 public abstract Drawable newDrawable(); 1257 1258 /** 1259 * Creates a new Drawable instance from its constant state using the 1260 * specified resources. This method should be implemented for drawables 1261 * that have density-dependent properties. 1262 * <p> 1263 * The default implementation for this method calls through to 1264 * {@link #newDrawable()}. 1265 * 1266 * @param res the resources of the context in which the drawable will 1267 * be displayed 1268 * @return a new drawable object based on this constant state 1269 */ 1270 @NonNull 1271 public Drawable newDrawable(@Nullable Resources res) { 1272 return newDrawable(); 1273 } 1274 1275 /** 1276 * Creates a new Drawable instance from its constant state using the 1277 * specified resources and theme. This method should be implemented for 1278 * drawables that have theme-dependent properties. 1279 * <p> 1280 * The default implementation for this method calls through to 1281 * {@link #newDrawable(Resources)}. 1282 * 1283 * @param res the resources of the context in which the drawable will 1284 * be displayed 1285 * @param theme the theme of the context in which the drawable will be 1286 * displayed 1287 * @return a new drawable object based on this constant state 1288 */ 1289 @NonNull 1290 public Drawable newDrawable(@Nullable Resources res, @Nullable Theme theme) { 1291 return newDrawable(res); 1292 } 1293 1294 /** 1295 * Return a bit mask of configuration changes that will impact 1296 * this drawable (and thus require completely reloading it). 1297 */ 1298 public abstract @Config int getChangingConfigurations(); 1299 1300 /** 1301 * @return Total pixel count 1302 * @hide 1303 */ 1304 public int addAtlasableBitmaps(Collection<Bitmap> atlasList) { 1305 return 0; 1306 } 1307 1308 /** @hide */ 1309 protected final boolean isAtlasable(Bitmap bitmap) { 1310 return bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888; 1311 } 1312 1313 /** 1314 * Return whether this constant state can have a theme applied. 1315 */ 1316 public boolean canApplyTheme() { 1317 return false; 1318 } 1319 } 1320 1321 /** 1322 * Return a {@link ConstantState} instance that holds the shared state of this Drawable. 1323 * 1324 * @return The ConstantState associated to that Drawable. 1325 * @see ConstantState 1326 * @see Drawable#mutate() 1327 */ 1328 public ConstantState getConstantState() { 1329 return null; 1330 } 1331 1332 private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np, 1333 Rect pad, Rect layoutBounds, String srcName) { 1334 1335 if (np != null) { 1336 return new NinePatchDrawable(res, bm, np, pad, layoutBounds, srcName); 1337 } 1338 1339 return new BitmapDrawable(res, bm); 1340 } 1341 1342 /** 1343 * Ensures the tint filter is consistent with the current tint color and 1344 * mode. 1345 */ 1346 PorterDuffColorFilter updateTintFilter(PorterDuffColorFilter tintFilter, ColorStateList tint, 1347 PorterDuff.Mode tintMode) { 1348 if (tint == null || tintMode == null) { 1349 return null; 1350 } 1351 1352 final int color = tint.getColorForState(getState(), Color.TRANSPARENT); 1353 if (tintFilter == null) { 1354 return new PorterDuffColorFilter(color, tintMode); 1355 } 1356 1357 tintFilter.setColor(color); 1358 tintFilter.setMode(tintMode); 1359 return tintFilter; 1360 } 1361 1362 /** 1363 * Obtains styled attributes from the theme, if available, or unstyled 1364 * resources if the theme is null. 1365 */ 1366 static TypedArray obtainAttributes( 1367 Resources res, Theme theme, AttributeSet set, int[] attrs) { 1368 if (theme == null) { 1369 return res.obtainAttributes(set, attrs); 1370 } 1371 return theme.obtainStyledAttributes(set, attrs, 0, 0); 1372 } 1373 1374 /** 1375 * Scales a floating-point pixel value from the source density to the 1376 * target density. 1377 * 1378 * @param pixels the pixel value for use in source density 1379 * @param sourceDensity the source density 1380 * @param targetDensity the target density 1381 * @return the scaled pixel value for use in target density 1382 */ 1383 static float scaleFromDensity(float pixels, int sourceDensity, int targetDensity) { 1384 return pixels * targetDensity / sourceDensity; 1385 } 1386 1387 /** 1388 * Scales a pixel value from the source density to the target density, 1389 * optionally handling the resulting pixel value as a size rather than an 1390 * offset. 1391 * <p> 1392 * A size conversion involves rounding the base value and ensuring that 1393 * a non-zero base value is at least one pixel in size. 1394 * <p> 1395 * An offset conversion involves simply truncating the base value to an 1396 * integer. 1397 * 1398 * @param pixels the pixel value for use in source density 1399 * @param sourceDensity the source density 1400 * @param targetDensity the target density 1401 * @param isSize {@code true} to handle the resulting scaled value as a 1402 * size, or {@code false} to handle it as an offset 1403 * @return the scaled pixel value for use in target density 1404 */ 1405 static int scaleFromDensity( 1406 int pixels, int sourceDensity, int targetDensity, boolean isSize) { 1407 if (pixels == 0 || sourceDensity == targetDensity) { 1408 return pixels; 1409 } 1410 1411 final float result = pixels * targetDensity / (float) sourceDensity; 1412 if (!isSize) { 1413 return (int) result; 1414 } 1415 1416 final int rounded = Math.round(result); 1417 if (rounded != 0) { 1418 return rounded; 1419 } else if (pixels == 0) { 1420 return 0; 1421 } else if (pixels > 0) { 1422 return 1; 1423 } else { 1424 return -1; 1425 } 1426 } 1427 1428 static int resolveDensity(@Nullable Resources r, int parentDensity) { 1429 final int densityDpi = r == null ? parentDensity : r.getDisplayMetrics().densityDpi; 1430 return densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi; 1431 } 1432 1433 /** 1434 * Re-throws an exception as a {@link RuntimeException} with an empty stack 1435 * trace to avoid cluttering the log. The original exception's stack trace 1436 * will still be included. 1437 * 1438 * @param cause the exception to re-throw 1439 * @throws RuntimeException 1440 */ 1441 static void rethrowAsRuntimeException(Exception cause) throws RuntimeException { 1442 final RuntimeException e = new RuntimeException(cause); 1443 e.setStackTrace(new StackTraceElement[0]); 1444 throw e; 1445 } 1446 1447 /** 1448 * Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode 1449 * attribute's enum value. 1450 * 1451 * @hide 1452 */ 1453 public static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) { 1454 switch (value) { 1455 case 3: return Mode.SRC_OVER; 1456 case 5: return Mode.SRC_IN; 1457 case 9: return Mode.SRC_ATOP; 1458 case 14: return Mode.MULTIPLY; 1459 case 15: return Mode.SCREEN; 1460 case 16: return Mode.ADD; 1461 default: return defaultMode; 1462 } 1463 } 1464} 1465 1466