• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.graphics.drawable;
18 
19 import android.graphics.Insets;
20 import org.xmlpull.v1.XmlPullParser;
21 import org.xmlpull.v1.XmlPullParserException;
22 
23 import android.content.res.Resources;
24 import android.content.res.TypedArray;
25 import android.graphics.Bitmap;
26 import android.graphics.BitmapFactory;
27 import android.graphics.Canvas;
28 import android.graphics.ColorFilter;
29 import android.graphics.NinePatch;
30 import android.graphics.PixelFormat;
31 import android.graphics.PorterDuff;
32 import android.graphics.PorterDuffColorFilter;
33 import android.graphics.Rect;
34 import android.graphics.Region;
35 import android.util.AttributeSet;
36 import android.util.DisplayMetrics;
37 import android.util.StateSet;
38 import android.util.TypedValue;
39 import android.util.Xml;
40 
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.lang.ref.WeakReference;
44 import java.util.Arrays;
45 
46 /**
47  * A Drawable is a general abstraction for "something that can be drawn."  Most
48  * often you will deal with Drawable as the type of resource retrieved for
49  * drawing things to the screen; the Drawable class provides a generic API for
50  * dealing with an underlying visual resource that may take a variety of forms.
51  * Unlike a {@link android.view.View}, a Drawable does not have any facility to
52  * receive events or otherwise interact with the user.
53  *
54  * <p>In addition to simple drawing, Drawable provides a number of generic
55  * mechanisms for its client to interact with what is being drawn:
56  *
57  * <ul>
58  *     <li> The {@link #setBounds} method <var>must</var> be called to tell the
59  *     Drawable where it is drawn and how large it should be.  All Drawables
60  *     should respect the requested size, often simply by scaling their
61  *     imagery.  A client can find the preferred size for some Drawables with
62  *     the {@link #getIntrinsicHeight} and {@link #getIntrinsicWidth} methods.
63  *
64  *     <li> The {@link #getPadding} method can return from some Drawables
65  *     information about how to frame content that is placed inside of them.
66  *     For example, a Drawable that is intended to be the frame for a button
67  *     widget would need to return padding that correctly places the label
68  *     inside of itself.
69  *
70  *     <li> The {@link #setState} method allows the client to tell the Drawable
71  *     in which state it is to be drawn, such as "focused", "selected", etc.
72  *     Some drawables may modify their imagery based on the selected state.
73  *
74  *     <li> The {@link #setLevel} method allows the client to supply a single
75  *     continuous controller that can modify the Drawable is displayed, such as
76  *     a battery level or progress level.  Some drawables may modify their
77  *     imagery based on the current level.
78  *
79  *     <li> A Drawable can perform animations by calling back to its client
80  *     through the {@link Callback} interface.  All clients should support this
81  *     interface (via {@link #setCallback}) so that animations will work.  A
82  *     simple way to do this is through the system facilities such as
83  *     {@link android.view.View#setBackgroundDrawable(Drawable)} and
84  *     {@link android.widget.ImageView}.
85  * </ul>
86  *
87  * Though usually not visible to the application, Drawables may take a variety
88  * of forms:
89  *
90  * <ul>
91  *     <li> <b>Bitmap</b>: the simplest Drawable, a PNG or JPEG image.
92  *     <li> <b>Nine Patch</b>: an extension to the PNG format allows it to
93  *     specify information about how to stretch it and place things inside of
94  *     it.
95  *     <li> <b>Shape</b>: contains simple drawing commands instead of a raw
96  *     bitmap, allowing it to resize better in some cases.
97  *     <li> <b>Layers</b>: a compound drawable, which draws multiple underlying
98  *     drawables on top of each other.
99  *     <li> <b>States</b>: a compound drawable that selects one of a set of
100  *     drawables based on its state.
101  *     <li> <b>Levels</b>: a compound drawable that selects one of a set of
102  *     drawables based on its level.
103  *     <li> <b>Scale</b>: a compound drawable with a single child drawable,
104  *     whose overall size is modified based on the current level.
105  * </ul>
106  *
107  * <div class="special reference">
108  * <h3>Developer Guides</h3>
109  * <p>For more information about how to use drawables, read the
110  * <a href="{@docRoot}guide/topics/graphics/2d-graphics.html">Canvas and Drawables</a> developer
111  * guide. For information and examples of creating drawable resources (XML or bitmap files that
112  * can be loaded in code), read the
113  * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>
114  * document.</p></div>
115  */
116 public abstract class Drawable {
117     private static final Rect ZERO_BOUNDS_RECT = new Rect();
118 
119     private int[] mStateSet = StateSet.WILD_CARD;
120     private int mLevel = 0;
121     private int mChangingConfigurations = 0;
122     private Rect mBounds = ZERO_BOUNDS_RECT;  // lazily becomes a new Rect()
123     private WeakReference<Callback> mCallback = null;
124     private boolean mVisible = true;
125 
126     private int mLayoutDirection;
127 
128     /**
129      * Draw in its bounds (set via setBounds) respecting optional effects such
130      * as alpha (set via setAlpha) and color filter (set via setColorFilter).
131      *
132      * @param canvas The canvas to draw into
133      */
draw(Canvas canvas)134     public abstract void draw(Canvas canvas);
135 
136     /**
137      * Specify a bounding rectangle for the Drawable. This is where the drawable
138      * will draw when its draw() method is called.
139      */
setBounds(int left, int top, int right, int bottom)140     public void setBounds(int left, int top, int right, int bottom) {
141         Rect oldBounds = mBounds;
142 
143         if (oldBounds == ZERO_BOUNDS_RECT) {
144             oldBounds = mBounds = new Rect();
145         }
146 
147         if (oldBounds.left != left || oldBounds.top != top ||
148                 oldBounds.right != right || oldBounds.bottom != bottom) {
149             mBounds.set(left, top, right, bottom);
150             onBoundsChange(mBounds);
151         }
152     }
153 
154     /**
155      * Specify a bounding rectangle for the Drawable. This is where the drawable
156      * will draw when its draw() method is called.
157      */
setBounds(Rect bounds)158     public void setBounds(Rect bounds) {
159         setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
160     }
161 
162     /**
163      * Return a copy of the drawable's bounds in the specified Rect (allocated
164      * by the caller). The bounds specify where this will draw when its draw()
165      * method is called.
166      *
167      * @param bounds Rect to receive the drawable's bounds (allocated by the
168      *               caller).
169      */
copyBounds(Rect bounds)170     public final void copyBounds(Rect bounds) {
171         bounds.set(mBounds);
172     }
173 
174     /**
175      * Return a copy of the drawable's bounds in a new Rect. This returns the
176      * same values as getBounds(), but the returned object is guaranteed to not
177      * be changed later by the drawable (i.e. it retains no reference to this
178      * rect). If the caller already has a Rect allocated, call copyBounds(rect).
179      *
180      * @return A copy of the drawable's bounds
181      */
copyBounds()182     public final Rect copyBounds() {
183         return new Rect(mBounds);
184     }
185 
186     /**
187      * Return the drawable's bounds Rect. Note: for efficiency, the returned
188      * object may be the same object stored in the drawable (though this is not
189      * guaranteed), so if a persistent copy of the bounds is needed, call
190      * copyBounds(rect) instead.
191      * You should also not change the object returned by this method as it may
192      * be the same object stored in the drawable.
193      *
194      * @return The bounds of the drawable (which may change later, so caller
195      *         beware). DO NOT ALTER the returned object as it may change the
196      *         stored bounds of this drawable.
197      *
198      * @see #copyBounds()
199      * @see #copyBounds(android.graphics.Rect)
200      */
getBounds()201     public final Rect getBounds() {
202         if (mBounds == ZERO_BOUNDS_RECT) {
203             mBounds = new Rect();
204         }
205 
206         return mBounds;
207     }
208 
209     /**
210      * Set a mask of the configuration parameters for which this drawable
211      * may change, requiring that it be re-created.
212      *
213      * @param configs A mask of the changing configuration parameters, as
214      * defined by {@link android.content.res.Configuration}.
215      *
216      * @see android.content.res.Configuration
217      */
setChangingConfigurations(int configs)218     public void setChangingConfigurations(int configs) {
219         mChangingConfigurations = configs;
220     }
221 
222     /**
223      * Return a mask of the configuration parameters for which this drawable
224      * may change, requiring that it be re-created.  The default implementation
225      * returns whatever was provided through
226      * {@link #setChangingConfigurations(int)} or 0 by default.  Subclasses
227      * may extend this to or in the changing configurations of any other
228      * drawables they hold.
229      *
230      * @return Returns a mask of the changing configuration parameters, as
231      * defined by {@link android.content.res.Configuration}.
232      *
233      * @see android.content.res.Configuration
234      */
getChangingConfigurations()235     public int getChangingConfigurations() {
236         return mChangingConfigurations;
237     }
238 
239     /**
240      * Set to true to have the drawable dither its colors when drawn to a device
241      * with fewer than 8-bits per color component. This can improve the look on
242      * those devices, but can also slow down the drawing a little.
243      */
setDither(boolean dither)244     public void setDither(boolean dither) {}
245 
246     /**
247      * Set to true to have the drawable filter its bitmap when scaled or rotated
248      * (for drawables that use bitmaps). If the drawable does not use bitmaps,
249      * this call is ignored. This can improve the look when scaled or rotated,
250      * but also slows down the drawing.
251      */
setFilterBitmap(boolean filter)252     public void setFilterBitmap(boolean filter) {}
253 
254     /**
255      * Implement this interface if you want to create an animated drawable that
256      * extends {@link android.graphics.drawable.Drawable Drawable}.
257      * Upon retrieving a drawable, use
258      * {@link Drawable#setCallback(android.graphics.drawable.Drawable.Callback)}
259      * to supply your implementation of the interface to the drawable; it uses
260      * this interface to schedule and execute animation changes.
261      */
262     public static interface Callback {
263         /**
264          * Called when the drawable needs to be redrawn.  A view at this point
265          * should invalidate itself (or at least the part of itself where the
266          * drawable appears).
267          *
268          * @param who The drawable that is requesting the update.
269          */
invalidateDrawable(Drawable who)270         public void invalidateDrawable(Drawable who);
271 
272         /**
273          * A Drawable can call this to schedule the next frame of its
274          * animation.  An implementation can generally simply call
275          * {@link android.os.Handler#postAtTime(Runnable, Object, long)} with
276          * the parameters <var>(what, who, when)</var> to perform the
277          * scheduling.
278          *
279          * @param who The drawable being scheduled.
280          * @param what The action to execute.
281          * @param when The time (in milliseconds) to run.  The timebase is
282          *             {@link android.os.SystemClock#uptimeMillis}
283          */
scheduleDrawable(Drawable who, Runnable what, long when)284         public void scheduleDrawable(Drawable who, Runnable what, long when);
285 
286         /**
287          * A Drawable can call this to unschedule an action previously
288          * scheduled with {@link #scheduleDrawable}.  An implementation can
289          * generally simply call
290          * {@link android.os.Handler#removeCallbacks(Runnable, Object)} with
291          * the parameters <var>(what, who)</var> to unschedule the drawable.
292          *
293          * @param who The drawable being unscheduled.
294          * @param what The action being unscheduled.
295          */
unscheduleDrawable(Drawable who, Runnable what)296         public void unscheduleDrawable(Drawable who, Runnable what);
297     }
298 
299     /**
300      * Bind a {@link Callback} object to this Drawable.  Required for clients
301      * that want to support animated drawables.
302      *
303      * @param cb The client's Callback implementation.
304      *
305      * @see #getCallback()
306      */
setCallback(Callback cb)307     public final void setCallback(Callback cb) {
308         mCallback = new WeakReference<Callback>(cb);
309     }
310 
311     /**
312      * Return the current {@link Callback} implementation attached to this
313      * Drawable.
314      *
315      * @return A {@link Callback} instance or null if no callback was set.
316      *
317      * @see #setCallback(android.graphics.drawable.Drawable.Callback)
318      */
getCallback()319     public Callback getCallback() {
320         if (mCallback != null) {
321             return mCallback.get();
322         }
323         return null;
324     }
325 
326     /**
327      * Use the current {@link Callback} implementation to have this Drawable
328      * redrawn.  Does nothing if there is no Callback attached to the
329      * Drawable.
330      *
331      * @see Callback#invalidateDrawable
332      * @see #getCallback()
333      * @see #setCallback(android.graphics.drawable.Drawable.Callback)
334      */
invalidateSelf()335     public void invalidateSelf() {
336         final Callback callback = getCallback();
337         if (callback != null) {
338             callback.invalidateDrawable(this);
339         }
340     }
341 
342     /**
343      * Use the current {@link Callback} implementation to have this Drawable
344      * scheduled.  Does nothing if there is no Callback attached to the
345      * Drawable.
346      *
347      * @param what The action being scheduled.
348      * @param when The time (in milliseconds) to run.
349      *
350      * @see Callback#scheduleDrawable
351      */
scheduleSelf(Runnable what, long when)352     public void scheduleSelf(Runnable what, long when) {
353         final Callback callback = getCallback();
354         if (callback != null) {
355             callback.scheduleDrawable(this, what, when);
356         }
357     }
358 
359     /**
360      * Use the current {@link Callback} implementation to have this Drawable
361      * unscheduled.  Does nothing if there is no Callback attached to the
362      * Drawable.
363      *
364      * @param what The runnable that you no longer want called.
365      *
366      * @see Callback#unscheduleDrawable
367      */
unscheduleSelf(Runnable what)368     public void unscheduleSelf(Runnable what) {
369         final Callback callback = getCallback();
370         if (callback != null) {
371             callback.unscheduleDrawable(this, what);
372         }
373     }
374 
375     /**
376      * Returns the resolved layout direction for this Drawable.
377      *
378      * @return One of {@link android.view.View#LAYOUT_DIRECTION_LTR},
379      *   {@link android.view.View#LAYOUT_DIRECTION_RTL}
380      *
381      * @hide
382      */
getLayoutDirection()383     public int getLayoutDirection() {
384         return mLayoutDirection;
385     }
386 
387     /**
388      * Set the layout direction for this drawable. Should be a resolved direction as the
389      * Drawable as no capacity to do the resolution on his own.
390      *
391      * @param layoutDirection One of {@link android.view.View#LAYOUT_DIRECTION_LTR},
392      *   {@link android.view.View#LAYOUT_DIRECTION_RTL}
393      *
394      * @hide
395      */
setLayoutDirection(int layoutDirection)396     public void setLayoutDirection(int layoutDirection) {
397         if (getLayoutDirection() != layoutDirection) {
398             mLayoutDirection = layoutDirection;
399         }
400     }
401 
402     /**
403      * Specify an alpha value for the drawable. 0 means fully transparent, and
404      * 255 means fully opaque.
405      */
setAlpha(int alpha)406     public abstract void setAlpha(int alpha);
407 
408     /**
409      * Specify an optional colorFilter for the drawable. Pass null to remove
410      * any filters.
411     */
setColorFilter(ColorFilter cf)412     public abstract void setColorFilter(ColorFilter cf);
413 
414     /**
415      * Specify a color and porterduff mode to be the colorfilter for this
416      * drawable.
417      */
setColorFilter(int color, PorterDuff.Mode mode)418     public void setColorFilter(int color, PorterDuff.Mode mode) {
419         setColorFilter(new PorterDuffColorFilter(color, mode));
420     }
421 
clearColorFilter()422     public void clearColorFilter() {
423         setColorFilter(null);
424     }
425 
426     /**
427      * Indicates whether this view will change its appearance based on state.
428      * Clients can use this to determine whether it is necessary to calculate
429      * their state and call setState.
430      *
431      * @return True if this view changes its appearance based on state, false
432      *         otherwise.
433      *
434      * @see #setState(int[])
435      */
isStateful()436     public boolean isStateful() {
437         return false;
438     }
439 
440     /**
441      * Specify a set of states for the drawable. These are use-case specific,
442      * so see the relevant documentation. As an example, the background for
443      * widgets like Button understand the following states:
444      * [{@link android.R.attr#state_focused},
445      *  {@link android.R.attr#state_pressed}].
446      *
447      * <p>If the new state you are supplying causes the appearance of the
448      * Drawable to change, then it is responsible for calling
449      * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em>
450      * true will be returned from this function.
451      *
452      * <p>Note: The Drawable holds a reference on to <var>stateSet</var>
453      * until a new state array is given to it, so you must not modify this
454      * array during that time.</p>
455      *
456      * @param stateSet The new set of states to be displayed.
457      *
458      * @return Returns true if this change in state has caused the appearance
459      * of the Drawable to change (hence requiring an invalidate), otherwise
460      * returns false.
461      */
setState(final int[] stateSet)462     public boolean setState(final int[] stateSet) {
463         if (!Arrays.equals(mStateSet, stateSet)) {
464             mStateSet = stateSet;
465             return onStateChange(stateSet);
466         }
467         return false;
468     }
469 
470     /**
471      * Describes the current state, as a union of primitve states, such as
472      * {@link android.R.attr#state_focused},
473      * {@link android.R.attr#state_selected}, etc.
474      * Some drawables may modify their imagery based on the selected state.
475      * @return An array of resource Ids describing the current state.
476      */
getState()477     public int[] getState() {
478         return mStateSet;
479     }
480 
481     /**
482      * If this Drawable does transition animations between states, ask that
483      * it immediately jump to the current state and skip any active animations.
484      */
jumpToCurrentState()485     public void jumpToCurrentState() {
486     }
487 
488     /**
489      * @return The current drawable that will be used by this drawable. For simple drawables, this
490      *         is just the drawable itself. For drawables that change state like
491      *         {@link StateListDrawable} and {@link LevelListDrawable} this will be the child drawable
492      *         currently in use.
493      */
getCurrent()494     public Drawable getCurrent() {
495         return this;
496     }
497 
498     /**
499      * Specify the level for the drawable.  This allows a drawable to vary its
500      * imagery based on a continuous controller, for example to show progress
501      * or volume level.
502      *
503      * <p>If the new level you are supplying causes the appearance of the
504      * Drawable to change, then it is responsible for calling
505      * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em>
506      * true will be returned from this function.
507      *
508      * @param level The new level, from 0 (minimum) to 10000 (maximum).
509      *
510      * @return Returns true if this change in level has caused the appearance
511      * of the Drawable to change (hence requiring an invalidate), otherwise
512      * returns false.
513      */
setLevel(int level)514     public final boolean setLevel(int level) {
515         if (mLevel != level) {
516             mLevel = level;
517             return onLevelChange(level);
518         }
519         return false;
520     }
521 
522     /**
523      * Retrieve the current level.
524      *
525      * @return int Current level, from 0 (minimum) to 10000 (maximum).
526      */
getLevel()527     public final int getLevel() {
528         return mLevel;
529     }
530 
531     /**
532      * Set whether this Drawable is visible.  This generally does not impact
533      * the Drawable's behavior, but is a hint that can be used by some
534      * Drawables, for example, to decide whether run animations.
535      *
536      * @param visible Set to true if visible, false if not.
537      * @param restart You can supply true here to force the drawable to behave
538      *                as if it has just become visible, even if it had last
539      *                been set visible.  Used for example to force animations
540      *                to restart.
541      *
542      * @return boolean Returns true if the new visibility is different than
543      *         its previous state.
544      */
setVisible(boolean visible, boolean restart)545     public boolean setVisible(boolean visible, boolean restart) {
546         boolean changed = mVisible != visible;
547         if (changed) {
548             mVisible = visible;
549             invalidateSelf();
550         }
551         return changed;
552     }
553 
isVisible()554     public final boolean isVisible() {
555         return mVisible;
556     }
557 
558     /**
559      * Return the opacity/transparency of this Drawable.  The returned value is
560      * one of the abstract format constants in
561      * {@link android.graphics.PixelFormat}:
562      * {@link android.graphics.PixelFormat#UNKNOWN},
563      * {@link android.graphics.PixelFormat#TRANSLUCENT},
564      * {@link android.graphics.PixelFormat#TRANSPARENT}, or
565      * {@link android.graphics.PixelFormat#OPAQUE}.
566      *
567      * <p>Generally a Drawable should be as conservative as possible with the
568      * value it returns.  For example, if it contains multiple child drawables
569      * and only shows one of them at a time, if only one of the children is
570      * TRANSLUCENT and the others are OPAQUE then TRANSLUCENT should be
571      * returned.  You can use the method {@link #resolveOpacity} to perform a
572      * standard reduction of two opacities to the appropriate single output.
573      *
574      * <p>Note that the returned value does <em>not</em> take into account a
575      * custom alpha or color filter that has been applied by the client through
576      * the {@link #setAlpha} or {@link #setColorFilter} methods.
577      *
578      * @return int The opacity class of the Drawable.
579      *
580      * @see android.graphics.PixelFormat
581      */
getOpacity()582     public abstract int getOpacity();
583 
584     /**
585      * Return the appropriate opacity value for two source opacities.  If
586      * either is UNKNOWN, that is returned; else, if either is TRANSLUCENT,
587      * that is returned; else, if either is TRANSPARENT, that is returned;
588      * else, OPAQUE is returned.
589      *
590      * <p>This is to help in implementing {@link #getOpacity}.
591      *
592      * @param op1 One opacity value.
593      * @param op2 Another opacity value.
594      *
595      * @return int The combined opacity value.
596      *
597      * @see #getOpacity
598      */
resolveOpacity(int op1, int op2)599     public static int resolveOpacity(int op1, int op2) {
600         if (op1 == op2) {
601             return op1;
602         }
603         if (op1 == PixelFormat.UNKNOWN || op2 == PixelFormat.UNKNOWN) {
604             return PixelFormat.UNKNOWN;
605         }
606         if (op1 == PixelFormat.TRANSLUCENT || op2 == PixelFormat.TRANSLUCENT) {
607             return PixelFormat.TRANSLUCENT;
608         }
609         if (op1 == PixelFormat.TRANSPARENT || op2 == PixelFormat.TRANSPARENT) {
610             return PixelFormat.TRANSPARENT;
611         }
612         return PixelFormat.OPAQUE;
613     }
614 
615     /**
616      * Returns a Region representing the part of the Drawable that is completely
617      * transparent.  This can be used to perform drawing operations, identifying
618      * which parts of the target will not change when rendering the Drawable.
619      * The default implementation returns null, indicating no transparent
620      * region; subclasses can optionally override this to return an actual
621      * Region if they want to supply this optimization information, but it is
622      * not required that they do so.
623      *
624      * @return Returns null if the Drawables has no transparent region to
625      * report, else a Region holding the parts of the Drawable's bounds that
626      * are transparent.
627      */
getTransparentRegion()628     public Region getTransparentRegion() {
629         return null;
630     }
631 
632     /**
633      * Override this in your subclass to change appearance if you recognize the
634      * specified state.
635      *
636      * @return Returns true if the state change has caused the appearance of
637      * the Drawable to change (that is, it needs to be drawn), else false
638      * if it looks the same and there is no need to redraw it since its
639      * last state.
640      */
onStateChange(int[] state)641     protected boolean onStateChange(int[] state) { return false; }
642     /** Override this in your subclass to change appearance if you vary based
643      *  on level.
644      * @return Returns true if the level change has caused the appearance of
645      * the Drawable to change (that is, it needs to be drawn), else false
646      * if it looks the same and there is no need to redraw it since its
647      * last level.
648      */
onLevelChange(int level)649     protected boolean onLevelChange(int level) { return false; }
650     /**
651      * Override this in your subclass to change appearance if you recognize the
652      * specified state.
653      */
onBoundsChange(Rect bounds)654     protected void onBoundsChange(Rect bounds) {}
655 
656     /**
657      * Return the intrinsic width of the underlying drawable object.  Returns
658      * -1 if it has no intrinsic width, such as with a solid color.
659      */
getIntrinsicWidth()660     public int getIntrinsicWidth() {
661         return -1;
662     }
663 
664     /**
665      * Return the intrinsic height of the underlying drawable object. Returns
666      * -1 if it has no intrinsic height, such as with a solid color.
667      */
getIntrinsicHeight()668     public int getIntrinsicHeight() {
669         return -1;
670     }
671 
672     /**
673      * Returns the minimum width suggested by this Drawable. If a View uses this
674      * Drawable as a background, it is suggested that the View use at least this
675      * value for its width. (There will be some scenarios where this will not be
676      * possible.) This value should INCLUDE any padding.
677      *
678      * @return The minimum width suggested by this Drawable. If this Drawable
679      *         doesn't have a suggested minimum width, 0 is returned.
680      */
getMinimumWidth()681     public int getMinimumWidth() {
682         final int intrinsicWidth = getIntrinsicWidth();
683         return intrinsicWidth > 0 ? intrinsicWidth : 0;
684     }
685 
686     /**
687      * Returns the minimum height suggested by this Drawable. If a View uses this
688      * Drawable as a background, it is suggested that the View use at least this
689      * value for its height. (There will be some scenarios where this will not be
690      * possible.) This value should INCLUDE any padding.
691      *
692      * @return The minimum height suggested by this Drawable. If this Drawable
693      *         doesn't have a suggested minimum height, 0 is returned.
694      */
getMinimumHeight()695     public int getMinimumHeight() {
696         final int intrinsicHeight = getIntrinsicHeight();
697         return intrinsicHeight > 0 ? intrinsicHeight : 0;
698     }
699 
700     /**
701      * Return in padding the insets suggested by this Drawable for placing
702      * content inside the drawable's bounds. Positive values move toward the
703      * center of the Drawable (set Rect.inset). Returns true if this drawable
704      * actually has a padding, else false. When false is returned, the padding
705      * is always set to 0.
706      */
getPadding(Rect padding)707     public boolean getPadding(Rect padding) {
708         padding.set(0, 0, 0, 0);
709         return false;
710     }
711 
712     /**
713      * Return in insets the layout insets suggested by this Drawable for use with alignment
714      * operations during layout.
715      *
716      * @hide
717      */
getLayoutInsets()718     public Insets getLayoutInsets() {
719         return Insets.NONE;
720     }
721 
722     /**
723      * Make this drawable mutable. This operation cannot be reversed. A mutable
724      * drawable is guaranteed to not share its state with any other drawable.
725      * This is especially useful when you need to modify properties of drawables
726      * loaded from resources. By default, all drawables instances loaded from
727      * the same resource share a common state; if you modify the state of one
728      * instance, all the other instances will receive the same modification.
729      *
730      * Calling this method on a mutable Drawable will have no effect.
731      *
732      * @return This drawable.
733      * @see ConstantState
734      * @see #getConstantState()
735      */
mutate()736     public Drawable mutate() {
737         return this;
738     }
739 
740     /**
741      * Create a drawable from an inputstream
742      */
createFromStream(InputStream is, String srcName)743     public static Drawable createFromStream(InputStream is, String srcName) {
744         return createFromResourceStream(null, null, is, srcName, null);
745     }
746 
747     /**
748      * Create a drawable from an inputstream, using the given resources and
749      * value to determine density information.
750      */
createFromResourceStream(Resources res, TypedValue value, InputStream is, String srcName)751     public static Drawable createFromResourceStream(Resources res, TypedValue value,
752             InputStream is, String srcName) {
753         return createFromResourceStream(res, value, is, srcName, null);
754     }
755 
756     /**
757      * Create a drawable from an inputstream, using the given resources and
758      * value to determine density information.
759      */
createFromResourceStream(Resources res, TypedValue value, InputStream is, String srcName, BitmapFactory.Options opts)760     public static Drawable createFromResourceStream(Resources res, TypedValue value,
761             InputStream is, String srcName, BitmapFactory.Options opts) {
762 
763         if (is == null) {
764             return null;
765         }
766 
767         /*  ugh. The decodeStream contract is that we have already allocated
768             the pad rect, but if the bitmap does not had a ninepatch chunk,
769             then the pad will be ignored. If we could change this to lazily
770             alloc/assign the rect, we could avoid the GC churn of making new
771             Rects only to drop them on the floor.
772         */
773         Rect pad = new Rect();
774 
775         // Special stuff for compatibility mode: if the target density is not
776         // the same as the display density, but the resource -is- the same as
777         // the display density, then don't scale it down to the target density.
778         // This allows us to load the system's density-correct resources into
779         // an application in compatibility mode, without scaling those down
780         // to the compatibility density only to have them scaled back up when
781         // drawn to the screen.
782         if (opts == null) opts = new BitmapFactory.Options();
783         opts.inScreenDensity = res != null
784                 ? res.getDisplayMetrics().noncompatDensityDpi : DisplayMetrics.DENSITY_DEVICE;
785         Bitmap  bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
786         if (bm != null) {
787             byte[] np = bm.getNinePatchChunk();
788             if (np == null || !NinePatch.isNinePatchChunk(np)) {
789                 np = null;
790                 pad = null;
791             }
792             int[] layoutBounds = bm.getLayoutBounds();
793             Rect layoutBoundsRect = null;
794             if (layoutBounds != null) {
795                 layoutBoundsRect = new Rect(layoutBounds[0], layoutBounds[1],
796                                              layoutBounds[2], layoutBounds[3]);
797             }
798             return drawableFromBitmap(res, bm, np, pad, layoutBoundsRect, srcName);
799         }
800         return null;
801     }
802 
803     /**
804      * Create a drawable from an XML document. For more information on how to
805      * create resources in XML, see
806      * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
807      */
createFromXml(Resources r, XmlPullParser parser)808     public static Drawable createFromXml(Resources r, XmlPullParser parser)
809             throws XmlPullParserException, IOException {
810         AttributeSet attrs = Xml.asAttributeSet(parser);
811 
812         int type;
813         while ((type=parser.next()) != XmlPullParser.START_TAG &&
814                 type != XmlPullParser.END_DOCUMENT) {
815             // Empty loop
816         }
817 
818         if (type != XmlPullParser.START_TAG) {
819             throw new XmlPullParserException("No start tag found");
820         }
821 
822         Drawable drawable = createFromXmlInner(r, parser, attrs);
823 
824         if (drawable == null) {
825             throw new RuntimeException("Unknown initial tag: " + parser.getName());
826         }
827 
828         return drawable;
829     }
830 
831     /**
832      * Create from inside an XML document.  Called on a parser positioned at
833      * a tag in an XML document, tries to create a Drawable from that tag.
834      * Returns null if the tag is not a valid drawable.
835      */
createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)836     public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)
837     throws XmlPullParserException, IOException {
838         Drawable drawable;
839 
840         final String name = parser.getName();
841 
842         if (name.equals("selector")) {
843             drawable = new StateListDrawable();
844         } else if (name.equals("level-list")) {
845             drawable = new LevelListDrawable();
846         /* Probably not doing this.
847         } else if (name.equals("mipmap")) {
848             drawable = new MipmapDrawable();
849         */
850         } else if (name.equals("layer-list")) {
851             drawable = new LayerDrawable();
852         } else if (name.equals("transition")) {
853             drawable = new TransitionDrawable();
854         } else if (name.equals("color")) {
855             drawable = new ColorDrawable();
856         } else if (name.equals("shape")) {
857             drawable = new GradientDrawable();
858         } else if (name.equals("scale")) {
859             drawable = new ScaleDrawable();
860         } else if (name.equals("clip")) {
861             drawable = new ClipDrawable();
862         } else if (name.equals("rotate")) {
863             drawable = new RotateDrawable();
864         } else if (name.equals("animated-rotate")) {
865             drawable = new AnimatedRotateDrawable();
866         } else if (name.equals("animation-list")) {
867             drawable = new AnimationDrawable();
868         } else if (name.equals("inset")) {
869             drawable = new InsetDrawable();
870         } else if (name.equals("bitmap")) {
871             drawable = new BitmapDrawable(r);
872             if (r != null) {
873                ((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
874             }
875         } else if (name.equals("nine-patch")) {
876             drawable = new NinePatchDrawable();
877             if (r != null) {
878                 ((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
879              }
880         } else {
881             throw new XmlPullParserException(parser.getPositionDescription() +
882                     ": invalid drawable tag " + name);
883         }
884 
885         drawable.inflate(r, parser, attrs);
886         return drawable;
887     }
888 
889 
890     /**
891      * Create a drawable from file path name.
892      */
createFromPath(String pathName)893     public static Drawable createFromPath(String pathName) {
894         if (pathName == null) {
895             return null;
896         }
897 
898         Bitmap bm = BitmapFactory.decodeFile(pathName);
899         if (bm != null) {
900             return drawableFromBitmap(null, bm, null, null, null, pathName);
901         }
902 
903         return null;
904     }
905 
906     /**
907      * Inflate this Drawable from an XML resource.
908      */
inflate(Resources r, XmlPullParser parser, AttributeSet attrs)909     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
910             throws XmlPullParserException, IOException {
911 
912         TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.Drawable);
913         inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.Drawable_visible);
914         a.recycle();
915     }
916 
917     /**
918      * Inflate a Drawable from an XML resource.
919      *
920      * @throws XmlPullParserException
921      * @throws IOException
922      */
inflateWithAttributes(Resources r, XmlPullParser parser, TypedArray attrs, int visibleAttr)923     void inflateWithAttributes(Resources r, XmlPullParser parser,
924             TypedArray attrs, int visibleAttr)
925             throws XmlPullParserException, IOException {
926 
927         mVisible = attrs.getBoolean(visibleAttr, mVisible);
928     }
929 
930     /**
931      * This abstract class is used by {@link Drawable}s to store shared constant state and data
932      * between Drawables. {@link BitmapDrawable}s created from the same resource will for instance
933      * share a unique bitmap stored in their ConstantState.
934      *
935      * <p>
936      * {@link #newDrawable(Resources)} can be used as a factory to create new Drawable instances
937      * from this ConstantState.
938      * </p>
939      *
940      * Use {@link Drawable#getConstantState()} to retrieve the ConstantState of a Drawable. Calling
941      * {@link Drawable#mutate()} on a Drawable should typically create a new ConstantState for that
942      * Drawable.
943      */
944     public static abstract class ConstantState {
945         /**
946          * Create a new drawable without supplying resources the caller
947          * is running in.  Note that using this means the density-dependent
948          * drawables (like bitmaps) will not be able to update their target
949          * density correctly. One should use {@link #newDrawable(Resources)}
950          * instead to provide a resource.
951          */
newDrawable()952         public abstract Drawable newDrawable();
953         /**
954          * Create a new Drawable instance from its constant state.  This
955          * must be implemented for drawables that change based on the target
956          * density of their caller (that is depending on whether it is
957          * in compatibility mode).
958          */
newDrawable(Resources res)959         public Drawable newDrawable(Resources res) {
960             return newDrawable();
961         }
962         /**
963          * Return a bit mask of configuration changes that will impact
964          * this drawable (and thus require completely reloading it).
965          */
getChangingConfigurations()966         public abstract int getChangingConfigurations();
967     }
968 
969     /**
970      * Return a {@link ConstantState} instance that holds the shared state of this Drawable.
971      *q
972      * @return The ConstantState associated to that Drawable.
973      * @see ConstantState
974      * @see Drawable#mutate()
975      */
getConstantState()976     public ConstantState getConstantState() {
977         return null;
978     }
979 
drawableFromBitmap(Resources res, Bitmap bm, byte[] np, Rect pad, Rect layoutBounds, String srcName)980     private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,
981             Rect pad, Rect layoutBounds, String srcName) {
982 
983         if (np != null) {
984             return new NinePatchDrawable(res, bm, np, pad, layoutBounds, srcName);
985         }
986 
987         return new BitmapDrawable(res, bm);
988     }
989 }
990 
991