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