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