Files
frameworks_base/graphics/java/android/graphics/drawable/Drawable.java
Chet Haase 96a443b58d Clarify Drawable.getOpacity() docs
Docs for translucent-vs-transparent-vs-opaque were confusing, especially since
the definition for those constants in PixelFormat are not the same as how they're
actually used in Drawable. This fix simply adds clarifying text to the method
comment for getOpacity().

Issue #21158891  Better document Drawable#getOpacity

Change-Id: I94725592f85e5d7874e3a3ac1c5bab969163fbf0
2015-06-12 17:21:31 +01:00

1433 lines
52 KiB
Java

/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics.drawable;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Xfermode;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.Xml;
import android.view.View;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collection;
/**
* A Drawable is a general abstraction for "something that can be drawn." Most
* often you will deal with Drawable as the type of resource retrieved for
* drawing things to the screen; the Drawable class provides a generic API for
* dealing with an underlying visual resource that may take a variety of forms.
* Unlike a {@link android.view.View}, a Drawable does not have any facility to
* receive events or otherwise interact with the user.
*
* <p>In addition to simple drawing, Drawable provides a number of generic
* mechanisms for its client to interact with what is being drawn:
*
* <ul>
* <li> The {@link #setBounds} method <var>must</var> be called to tell the
* Drawable where it is drawn and how large it should be. All Drawables
* should respect the requested size, often simply by scaling their
* imagery. A client can find the preferred size for some Drawables with
* the {@link #getIntrinsicHeight} and {@link #getIntrinsicWidth} methods.
*
* <li> The {@link #getPadding} method can return from some Drawables
* information about how to frame content that is placed inside of them.
* For example, a Drawable that is intended to be the frame for a button
* widget would need to return padding that correctly places the label
* inside of itself.
*
* <li> The {@link #setState} method allows the client to tell the Drawable
* in which state it is to be drawn, such as "focused", "selected", etc.
* Some drawables may modify their imagery based on the selected state.
*
* <li> The {@link #setLevel} method allows the client to supply a single
* continuous controller that can modify the Drawable is displayed, such as
* a battery level or progress level. Some drawables may modify their
* imagery based on the current level.
*
* <li> A Drawable can perform animations by calling back to its client
* through the {@link Callback} interface. All clients should support this
* interface (via {@link #setCallback}) so that animations will work. A
* simple way to do this is through the system facilities such as
* {@link android.view.View#setBackground(Drawable)} and
* {@link android.widget.ImageView}.
* </ul>
*
* Though usually not visible to the application, Drawables may take a variety
* of forms:
*
* <ul>
* <li> <b>Bitmap</b>: the simplest Drawable, a PNG or JPEG image.
* <li> <b>Nine Patch</b>: an extension to the PNG format allows it to
* specify information about how to stretch it and place things inside of
* it.
* <li> <b>Shape</b>: contains simple drawing commands instead of a raw
* bitmap, allowing it to resize better in some cases.
* <li> <b>Layers</b>: a compound drawable, which draws multiple underlying
* drawables on top of each other.
* <li> <b>States</b>: a compound drawable that selects one of a set of
* drawables based on its state.
* <li> <b>Levels</b>: a compound drawable that selects one of a set of
* drawables based on its level.
* <li> <b>Scale</b>: a compound drawable with a single child drawable,
* whose overall size is modified based on the current level.
* </ul>
*
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about how to use drawables, read the
* <a href="{@docRoot}guide/topics/graphics/2d-graphics.html">Canvas and Drawables</a> developer
* guide. For information and examples of creating drawable resources (XML or bitmap files that
* can be loaded in code), read the
* <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>
* document.</p></div>
*/
public abstract class Drawable {
private static final Rect ZERO_BOUNDS_RECT = new Rect();
static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
private int[] mStateSet = StateSet.WILD_CARD;
private int mLevel = 0;
private int mChangingConfigurations = 0;
private Rect mBounds = ZERO_BOUNDS_RECT; // lazily becomes a new Rect()
private WeakReference<Callback> mCallback = null;
private boolean mVisible = true;
private int mLayoutDirection;
/**
* Draw in its bounds (set via setBounds) respecting optional effects such
* as alpha (set via setAlpha) and color filter (set via setColorFilter).
*
* @param canvas The canvas to draw into
*/
public abstract void draw(Canvas canvas);
/**
* Specify a bounding rectangle for the Drawable. This is where the drawable
* will draw when its draw() method is called.
*/
public void setBounds(int left, int top, int right, int bottom) {
Rect oldBounds = mBounds;
if (oldBounds == ZERO_BOUNDS_RECT) {
oldBounds = mBounds = new Rect();
}
if (oldBounds.left != left || oldBounds.top != top ||
oldBounds.right != right || oldBounds.bottom != bottom) {
if (!oldBounds.isEmpty()) {
// first invalidate the previous bounds
invalidateSelf();
}
mBounds.set(left, top, right, bottom);
onBoundsChange(mBounds);
}
}
/**
* Specify a bounding rectangle for the Drawable. This is where the drawable
* will draw when its draw() method is called.
*/
public void setBounds(Rect bounds) {
setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
}
/**
* Return a copy of the drawable's bounds in the specified Rect (allocated
* by the caller). The bounds specify where this will draw when its draw()
* method is called.
*
* @param bounds Rect to receive the drawable's bounds (allocated by the
* caller).
*/
public final void copyBounds(Rect bounds) {
bounds.set(mBounds);
}
/**
* Return a copy of the drawable's bounds in a new Rect. This returns the
* same values as getBounds(), but the returned object is guaranteed to not
* be changed later by the drawable (i.e. it retains no reference to this
* rect). If the caller already has a Rect allocated, call copyBounds(rect).
*
* @return A copy of the drawable's bounds
*/
public final Rect copyBounds() {
return new Rect(mBounds);
}
/**
* Return the drawable's bounds Rect. Note: for efficiency, the returned
* object may be the same object stored in the drawable (though this is not
* guaranteed), so if a persistent copy of the bounds is needed, call
* copyBounds(rect) instead.
* You should also not change the object returned by this method as it may
* be the same object stored in the drawable.
*
* @return The bounds of the drawable (which may change later, so caller
* beware). DO NOT ALTER the returned object as it may change the
* stored bounds of this drawable.
*
* @see #copyBounds()
* @see #copyBounds(android.graphics.Rect)
*/
public final Rect getBounds() {
if (mBounds == ZERO_BOUNDS_RECT) {
mBounds = new Rect();
}
return mBounds;
}
/**
* Return the drawable's dirty bounds Rect. Note: for efficiency, the
* returned object may be the same object stored in the drawable (though
* this is not guaranteed).
* <p>
* By default, this returns the full drawable bounds. Custom drawables may
* override this method to perform more precise invalidation.
*
* @return The dirty bounds of this drawable
*/
public Rect getDirtyBounds() {
return getBounds();
}
/**
* Set a mask of the configuration parameters for which this drawable
* may change, requiring that it be re-created.
*
* @param configs A mask of the changing configuration parameters, as
* defined by {@link android.content.pm.ActivityInfo}.
*
* @see android.content.pm.ActivityInfo
*/
public void setChangingConfigurations(int configs) {
mChangingConfigurations = configs;
}
/**
* Return a mask of the configuration parameters for which this drawable
* may change, requiring that it be re-created. The default implementation
* returns whatever was provided through
* {@link #setChangingConfigurations(int)} or 0 by default. Subclasses
* may extend this to or in the changing configurations of any other
* drawables they hold.
*
* @return Returns a mask of the changing configuration parameters, as
* defined by {@link android.content.pm.ActivityInfo}.
*
* @see android.content.pm.ActivityInfo
*/
public int getChangingConfigurations() {
return mChangingConfigurations;
}
/**
* Set to true to have the drawable dither its colors when drawn to a device
* with fewer than 8-bits per color component. This can improve the look on
* those devices, but can also slow down the drawing a little.
*/
public void setDither(boolean dither) {}
/**
* @return whether this drawable dithers its colors
* @see #setDither(boolean)
*/
public boolean getDither() {
return false;
}
/**
* Set to true to have the drawable filter its bitmap when scaled or rotated
* (for drawables that use bitmaps). If the drawable does not use bitmaps,
* this call is ignored. This can improve the look when scaled or rotated,
* but also slows down the drawing.
*/
public void setFilterBitmap(boolean filter) {}
/**
* @return whether this drawable filters its bitmap
* @see #setFilterBitmap(boolean)
*/
public boolean isFilterBitmap() {
return false;
}
/**
* Implement this interface if you want to create an animated drawable that
* extends {@link android.graphics.drawable.Drawable Drawable}.
* Upon retrieving a drawable, use
* {@link Drawable#setCallback(android.graphics.drawable.Drawable.Callback)}
* to supply your implementation of the interface to the drawable; it uses
* this interface to schedule and execute animation changes.
*/
public static interface Callback {
/**
* Called when the drawable needs to be redrawn. A view at this point
* should invalidate itself (or at least the part of itself where the
* drawable appears).
*
* @param who The drawable that is requesting the update.
*/
public void invalidateDrawable(Drawable who);
/**
* A Drawable can call this to schedule the next frame of its
* animation. An implementation can generally simply call
* {@link android.os.Handler#postAtTime(Runnable, Object, long)} with
* the parameters <var>(what, who, when)</var> to perform the
* scheduling.
*
* @param who The drawable being scheduled.
* @param what The action to execute.
* @param when The time (in milliseconds) to run. The timebase is
* {@link android.os.SystemClock#uptimeMillis}
*/
public void scheduleDrawable(Drawable who, Runnable what, long when);
/**
* A Drawable can call this to unschedule an action previously
* scheduled with {@link #scheduleDrawable}. An implementation can
* generally simply call
* {@link android.os.Handler#removeCallbacks(Runnable, Object)} with
* the parameters <var>(what, who)</var> to unschedule the drawable.
*
* @param who The drawable being unscheduled.
* @param what The action being unscheduled.
*/
public void unscheduleDrawable(Drawable who, Runnable what);
}
/**
* Bind a {@link Callback} object to this Drawable. Required for clients
* that want to support animated drawables.
*
* @param cb The client's Callback implementation.
*
* @see #getCallback()
*/
public final void setCallback(Callback cb) {
mCallback = new WeakReference<Callback>(cb);
}
/**
* Return the current {@link Callback} implementation attached to this
* Drawable.
*
* @return A {@link Callback} instance or null if no callback was set.
*
* @see #setCallback(android.graphics.drawable.Drawable.Callback)
*/
public Callback getCallback() {
if (mCallback != null) {
return mCallback.get();
}
return null;
}
/**
* Use the current {@link Callback} implementation to have this Drawable
* redrawn. Does nothing if there is no Callback attached to the
* Drawable.
*
* @see Callback#invalidateDrawable
* @see #getCallback()
* @see #setCallback(android.graphics.drawable.Drawable.Callback)
*/
public void invalidateSelf() {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
/**
* Use the current {@link Callback} implementation to have this Drawable
* scheduled. Does nothing if there is no Callback attached to the
* Drawable.
*
* @param what The action being scheduled.
* @param when The time (in milliseconds) to run.
*
* @see Callback#scheduleDrawable
*/
public void scheduleSelf(Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
}
}
/**
* Use the current {@link Callback} implementation to have this Drawable
* unscheduled. Does nothing if there is no Callback attached to the
* Drawable.
*
* @param what The runnable that you no longer want called.
*
* @see Callback#unscheduleDrawable
*/
public void unscheduleSelf(Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
callback.unscheduleDrawable(this, what);
}
}
/**
* Returns the resolved layout direction for this Drawable.
*
* @return One of {@link android.view.View#LAYOUT_DIRECTION_LTR},
* {@link android.view.View#LAYOUT_DIRECTION_RTL}
* @see #setLayoutDirection(int)
*/
public int getLayoutDirection() {
return mLayoutDirection;
}
/**
* Set the layout direction for this drawable. Should be a resolved
* layout direction, as the Drawable as no capacity to do the resolution on
* its own.
*
* @param layoutDirection the resolved layout direction for the drawable,
* either {@link android.view.View#LAYOUT_DIRECTION_LTR}
* or {@link android.view.View#LAYOUT_DIRECTION_RTL}
* @see #getLayoutDirection()
*/
public final boolean setLayoutDirection(@View.ResolvedLayoutDir int layoutDirection) {
if (mLayoutDirection != layoutDirection) {
mLayoutDirection = layoutDirection;
return onLayoutDirectionChanged(layoutDirection);
}
return false;
}
/**
* Called when the drawable's resolved layout direction changes.
*
* @param layoutDirection the new resolved layout direction
* @return true if the layout direction change has caused the appearance of
* the drawable to change and it needs to be re-drawn
* @see #setLayoutDirection(int)
*/
public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) {
return false;
}
/**
* Specify an alpha value for the drawable. 0 means fully transparent, and
* 255 means fully opaque.
*/
public abstract void setAlpha(int alpha);
/**
* Gets the current alpha value for the drawable. 0 means fully transparent,
* 255 means fully opaque. This method is implemented by
* Drawable subclasses and the value returned is specific to how that class treats alpha.
* The default return value is 255 if the class does not override this method to return a value
* specific to its use of alpha.
*/
public int getAlpha() {
return 0xFF;
}
/**
* @hide
*
* Internal-only method for setting xfermode on certain supported drawables.
*
* Should not be made public since the layers and drawing area with which
* Drawables draw is private implementation detail, and not something apps
* should rely upon.
*/
public void setXfermode(Xfermode mode) {
// Base implementation drops it on the floor for compatibility. Whee!
}
/**
* Specify an optional color filter for the drawable.
* <p>
* If a Drawable has a ColorFilter, each output pixel of the Drawable's
* drawing contents will be modified by the color filter before it is
* blended onto the render target of a Canvas.
* </p>
* <p>
* Pass {@code null} to remove any existing color filter.
* </p>
* <p class="note"><strong>Note:</strong> Setting a non-{@code null} color
* filter disables {@link #setTintList(ColorStateList) tint}.
* </p>
*
* @param colorFilter The color filter to apply, or {@code null} to remove the
* existing color filter
*/
public abstract void setColorFilter(@Nullable ColorFilter colorFilter);
/**
* Specify a color and Porter-Duff mode to be the color filter for this
* drawable.
* <p>
* Convenience for {@link #setColorFilter(ColorFilter)} which constructs a
* {@link PorterDuffColorFilter}.
* </p>
* <p class="note"><strong>Note:</strong> Setting a color filter disables
* {@link #setTintList(ColorStateList) tint}.
* </p>
*/
public void setColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
setColorFilter(new PorterDuffColorFilter(color, mode));
}
/**
* Specifies tint color for this drawable.
* <p>
* A Drawable's drawing content will be blended together with its tint
* before it is drawn to the screen. This functions similarly to
* {@link #setColorFilter(int, PorterDuff.Mode)}.
* </p>
* <p>
* To clear the tint, pass {@code null} to
* {@link #setTintList(ColorStateList)}.
* </p>
* <p class="note"><strong>Note:</strong> Setting a color filter via
* {@link #setColorFilter(ColorFilter)} or
* {@link #setColorFilter(int, PorterDuff.Mode)} overrides tint.
* </p>
*
* @param tintColor Color to use for tinting this drawable
* @see #setTintList(ColorStateList)
* @see #setTintMode(PorterDuff.Mode)
*/
public void setTint(@ColorInt int tintColor) {
setTintList(ColorStateList.valueOf(tintColor));
}
/**
* Specifies tint color for this drawable as a color state list.
* <p>
* A Drawable's drawing content will be blended together with its tint
* before it is drawn to the screen. This functions similarly to
* {@link #setColorFilter(int, PorterDuff.Mode)}.
* </p>
* <p class="note"><strong>Note:</strong> Setting a color filter via
* {@link #setColorFilter(ColorFilter)} or
* {@link #setColorFilter(int, PorterDuff.Mode)} overrides tint.
* </p>
*
* @param tint Color state list to use for tinting this drawable, or
* {@code null} to clear the tint
* @see #setTint(int)
* @see #setTintMode(PorterDuff.Mode)
*/
public void setTintList(@Nullable ColorStateList tint) {}
/**
* Specifies a tint blending mode for this drawable.
* <p>
* Defines how this drawable's tint color should be blended into the drawable
* before it is drawn to screen. Default tint mode is {@link PorterDuff.Mode#SRC_IN}.
* </p>
* <p class="note"><strong>Note:</strong> Setting a color filter via
* {@link #setColorFilter(ColorFilter)} or
* {@link #setColorFilter(int, PorterDuff.Mode)} overrides tint.
* </p>
*
* @param tintMode A Porter-Duff blending mode
* @see #setTint(int)
* @see #setTintList(ColorStateList)
*/
public void setTintMode(@NonNull PorterDuff.Mode tintMode) {}
/**
* Returns the current color filter, or {@code null} if none set.
*
* @return the current color filter, or {@code null} if none set
*/
public ColorFilter getColorFilter() {
return null;
}
/**
* Removes the color filter for this drawable.
*/
public void clearColorFilter() {
setColorFilter(null);
}
/**
* Specifies the hotspot's location within the drawable.
*
* @param x The X coordinate of the center of the hotspot
* @param y The Y coordinate of the center of the hotspot
*/
public void setHotspot(float x, float y) {}
/**
* Sets the bounds to which the hotspot is constrained, if they should be
* different from the drawable bounds.
*
* @param left position in pixels of the left bound
* @param top position in pixels of the top bound
* @param right position in pixels of the right bound
* @param bottom position in pixels of the bottom bound
* @see #getHotspotBounds(android.graphics.Rect)
*/
public void setHotspotBounds(int left, int top, int right, int bottom) {}
/**
* Populates {@code outRect} with the hotspot bounds.
*
* @param outRect the rect to populate with the hotspot bounds
* @see #setHotspotBounds(int, int, int, int)
*/
public void getHotspotBounds(Rect outRect) {
outRect.set(getBounds());
}
/**
* Whether this drawable requests projection.
*
* @hide magic!
*/
public boolean isProjected() {
return false;
}
/**
* Indicates whether this drawable will change its appearance based on
* state. Clients can use this to determine whether it is necessary to
* calculate their state and call setState.
*
* @return True if this drawable changes its appearance based on state,
* false otherwise.
* @see #setState(int[])
*/
public boolean isStateful() {
return false;
}
/**
* Specify a set of states for the drawable. These are use-case specific,
* so see the relevant documentation. As an example, the background for
* widgets like Button understand the following states:
* [{@link android.R.attr#state_focused},
* {@link android.R.attr#state_pressed}].
*
* <p>If the new state you are supplying causes the appearance of the
* Drawable to change, then it is responsible for calling
* {@link #invalidateSelf} in order to have itself redrawn, <em>and</em>
* true will be returned from this function.
*
* <p>Note: The Drawable holds a reference on to <var>stateSet</var>
* until a new state array is given to it, so you must not modify this
* array during that time.</p>
*
* @param stateSet The new set of states to be displayed.
*
* @return Returns true if this change in state has caused the appearance
* of the Drawable to change (hence requiring an invalidate), otherwise
* returns false.
*/
public boolean setState(final int[] stateSet) {
if (!Arrays.equals(mStateSet, stateSet)) {
mStateSet = stateSet;
return onStateChange(stateSet);
}
return false;
}
/**
* Describes the current state, as a union of primitve states, such as
* {@link android.R.attr#state_focused},
* {@link android.R.attr#state_selected}, etc.
* Some drawables may modify their imagery based on the selected state.
* @return An array of resource Ids describing the current state.
*/
public int[] getState() {
return mStateSet;
}
/**
* If this Drawable does transition animations between states, ask that
* it immediately jump to the current state and skip any active animations.
*/
public void jumpToCurrentState() {
}
/**
* @return The current drawable that will be used by this drawable. For simple drawables, this
* is just the drawable itself. For drawables that change state like
* {@link StateListDrawable} and {@link LevelListDrawable} this will be the child drawable
* currently in use.
*/
public Drawable getCurrent() {
return this;
}
/**
* Specify the level for the drawable. This allows a drawable to vary its
* imagery based on a continuous controller, for example to show progress
* or volume level.
*
* <p>If the new level you are supplying causes the appearance of the
* Drawable to change, then it is responsible for calling
* {@link #invalidateSelf} in order to have itself redrawn, <em>and</em>
* true will be returned from this function.
*
* @param level The new level, from 0 (minimum) to 10000 (maximum).
*
* @return Returns true if this change in level has caused the appearance
* of the Drawable to change (hence requiring an invalidate), otherwise
* returns false.
*/
public final boolean setLevel(int level) {
if (mLevel != level) {
mLevel = level;
return onLevelChange(level);
}
return false;
}
/**
* Retrieve the current level.
*
* @return int Current level, from 0 (minimum) to 10000 (maximum).
*/
public final int getLevel() {
return mLevel;
}
/**
* Set whether this Drawable is visible. This generally does not impact
* the Drawable's behavior, but is a hint that can be used by some
* Drawables, for example, to decide whether run animations.
*
* @param visible Set to true if visible, false if not.
* @param restart You can supply true here to force the drawable to behave
* as if it has just become visible, even if it had last
* been set visible. Used for example to force animations
* to restart.
*
* @return boolean Returns true if the new visibility is different than
* its previous state.
*/
public boolean setVisible(boolean visible, boolean restart) {
boolean changed = mVisible != visible;
if (changed) {
mVisible = visible;
invalidateSelf();
}
return changed;
}
public final boolean isVisible() {
return mVisible;
}
/**
* Set whether this Drawable is automatically mirrored when its layout direction is RTL
* (right-to left). See {@link android.util.LayoutDirection}.
*
* @param mirrored Set to true if the Drawable should be mirrored, false if not.
*/
public void setAutoMirrored(boolean mirrored) {
}
/**
* Tells if this Drawable will be automatically mirrored when its layout direction is RTL
* right-to-left. See {@link android.util.LayoutDirection}.
*
* @return boolean Returns true if this Drawable will be automatically mirrored.
*/
public boolean isAutoMirrored() {
return false;
}
/**
* Applies the specified theme to this Drawable and its children.
*/
public void applyTheme(@SuppressWarnings("unused") Theme t) {
}
public boolean canApplyTheme() {
return false;
}
/**
* Return the opacity/transparency of this Drawable. The returned value is
* one of the abstract format constants in
* {@link android.graphics.PixelFormat}:
* {@link android.graphics.PixelFormat#UNKNOWN},
* {@link android.graphics.PixelFormat#TRANSLUCENT},
* {@link android.graphics.PixelFormat#TRANSPARENT}, or
* {@link android.graphics.PixelFormat#OPAQUE}.
*
* <p>An OPAQUE drawable is one that draws all all content within its bounds, completely
* covering anything behind the drawable. A TRANSPARENT drawable is one that draws nothing
* within its bounds, allowing everything behind it to show through. A TRANSLUCENT drawable
* is a drawable in any other state, where the drawable will draw some, but not all,
* of the content within its bounds and at least some content behind the drawable will
* be visible. If the visibility of the drawable's contents cannot be determined, the
* safest/best return value is TRANSLUCENT.
*
* <p>Generally a Drawable should be as conservative as possible with the
* value it returns. For example, if it contains multiple child drawables
* and only shows one of them at a time, if only one of the children is
* TRANSLUCENT and the others are OPAQUE then TRANSLUCENT should be
* returned. You can use the method {@link #resolveOpacity} to perform a
* standard reduction of two opacities to the appropriate single output.
*
* <p>Note that the returned value does not necessarily take into account a
* custom alpha or color filter that has been applied by the client through
* the {@link #setAlpha} or {@link #setColorFilter} methods. Some subclasses,
* such as {@link BitmapDrawable}, {@link ColorDrawable}, and {@link GradientDrawable},
* do account for the value of {@link #setAlpha}, but the general behavior is dependent
* upon the implementation of the subclass.
*
* @return int The opacity class of the Drawable.
*
* @see android.graphics.PixelFormat
*/
public abstract int getOpacity();
/**
* Return the appropriate opacity value for two source opacities. If
* either is UNKNOWN, that is returned; else, if either is TRANSLUCENT,
* that is returned; else, if either is TRANSPARENT, that is returned;
* else, OPAQUE is returned.
*
* <p>This is to help in implementing {@link #getOpacity}.
*
* @param op1 One opacity value.
* @param op2 Another opacity value.
*
* @return int The combined opacity value.
*
* @see #getOpacity
*/
public static int resolveOpacity(int op1, int op2) {
if (op1 == op2) {
return op1;
}
if (op1 == PixelFormat.UNKNOWN || op2 == PixelFormat.UNKNOWN) {
return PixelFormat.UNKNOWN;
}
if (op1 == PixelFormat.TRANSLUCENT || op2 == PixelFormat.TRANSLUCENT) {
return PixelFormat.TRANSLUCENT;
}
if (op1 == PixelFormat.TRANSPARENT || op2 == PixelFormat.TRANSPARENT) {
return PixelFormat.TRANSPARENT;
}
return PixelFormat.OPAQUE;
}
/**
* Returns a Region representing the part of the Drawable that is completely
* transparent. This can be used to perform drawing operations, identifying
* which parts of the target will not change when rendering the Drawable.
* The default implementation returns null, indicating no transparent
* region; subclasses can optionally override this to return an actual
* Region if they want to supply this optimization information, but it is
* not required that they do so.
*
* @return Returns null if the Drawables has no transparent region to
* report, else a Region holding the parts of the Drawable's bounds that
* are transparent.
*/
public Region getTransparentRegion() {
return null;
}
/**
* Override this in your subclass to change appearance if you recognize the
* specified state.
*
* @return Returns true if the state change has caused the appearance of
* the Drawable to change (that is, it needs to be drawn), else false
* if it looks the same and there is no need to redraw it since its
* last state.
*/
protected boolean onStateChange(int[] state) { return false; }
/** Override this in your subclass to change appearance if you vary based
* on level.
* @return Returns true if the level change has caused the appearance of
* the Drawable to change (that is, it needs to be drawn), else false
* if it looks the same and there is no need to redraw it since its
* last level.
*/
protected boolean onLevelChange(int level) { return false; }
/**
* Override this in your subclass to change appearance if you vary based on
* the bounds.
*/
protected void onBoundsChange(Rect bounds) {}
/**
* Return the intrinsic width of the underlying drawable object. Returns
* -1 if it has no intrinsic width, such as with a solid color.
*/
public int getIntrinsicWidth() {
return -1;
}
/**
* Return the intrinsic height of the underlying drawable object. Returns
* -1 if it has no intrinsic height, such as with a solid color.
*/
public int getIntrinsicHeight() {
return -1;
}
/**
* Returns the minimum width suggested by this Drawable. If a View uses this
* Drawable as a background, it is suggested that the View use at least this
* value for its width. (There will be some scenarios where this will not be
* possible.) This value should INCLUDE any padding.
*
* @return The minimum width suggested by this Drawable. If this Drawable
* doesn't have a suggested minimum width, 0 is returned.
*/
public int getMinimumWidth() {
final int intrinsicWidth = getIntrinsicWidth();
return intrinsicWidth > 0 ? intrinsicWidth : 0;
}
/**
* Returns the minimum height suggested by this Drawable. If a View uses this
* Drawable as a background, it is suggested that the View use at least this
* value for its height. (There will be some scenarios where this will not be
* possible.) This value should INCLUDE any padding.
*
* @return The minimum height suggested by this Drawable. If this Drawable
* doesn't have a suggested minimum height, 0 is returned.
*/
public int getMinimumHeight() {
final int intrinsicHeight = getIntrinsicHeight();
return intrinsicHeight > 0 ? intrinsicHeight : 0;
}
/**
* Return in padding the insets suggested by this Drawable for placing
* content inside the drawable's bounds. Positive values move toward the
* center of the Drawable (set Rect.inset).
*
* @return true if this drawable actually has a padding, else false. When false is returned,
* the padding is always set to 0.
*/
public boolean getPadding(@NonNull Rect padding) {
padding.set(0, 0, 0, 0);
return false;
}
/**
* Return in insets the layout insets suggested by this Drawable for use with alignment
* operations during layout.
*
* @hide
*/
public Insets getOpticalInsets() {
return Insets.NONE;
}
/**
* Called to get the drawable to populate the Outline that defines its drawing area.
* <p>
* This method is called by the default {@link android.view.ViewOutlineProvider} to define
* the outline of the View.
* <p>
* The default behavior defines the outline to be the bounding rectangle of 0 alpha.
* Subclasses that wish to convey a different shape or alpha value must override this method.
*
* @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider)
*/
public void getOutline(@NonNull Outline outline) {
outline.setRect(getBounds());
outline.setAlpha(0);
}
/**
* Make this drawable mutable. This operation cannot be reversed. A mutable
* drawable is guaranteed to not share its state with any other drawable.
* This is especially useful when you need to modify properties of drawables
* loaded from resources. By default, all drawables instances loaded from
* the same resource share a common state; if you modify the state of one
* instance, all the other instances will receive the same modification.
*
* Calling this method on a mutable Drawable will have no effect.
*
* @return This drawable.
* @see ConstantState
* @see #getConstantState()
*/
public Drawable mutate() {
return this;
}
/**
* Clears the mutated state, allowing this drawable to be cached and
* mutated again.
* <p>
* This is hidden because only framework drawables can be cached, so
* custom drawables don't need to support constant state, mutate(), or
* clearMutated().
*
* @hide
*/
public void clearMutated() {
// Default implementation is no-op.
}
/**
* Create a drawable from an inputstream
*/
public static Drawable createFromStream(InputStream is, String srcName) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
try {
return createFromResourceStream(null, null, is, srcName);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
/**
* Create a drawable from an inputstream, using the given resources and
* value to determine density information.
*/
public static Drawable createFromResourceStream(Resources res, TypedValue value,
InputStream is, String srcName) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
try {
return createFromResourceStream(res, value, is, srcName, null);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
/**
* Create a drawable from an inputstream, using the given resources and
* value to determine density information.
*/
public static Drawable createFromResourceStream(Resources res, TypedValue value,
InputStream is, String srcName, BitmapFactory.Options opts) {
if (is == null) {
return null;
}
/* ugh. The decodeStream contract is that we have already allocated
the pad rect, but if the bitmap does not had a ninepatch chunk,
then the pad will be ignored. If we could change this to lazily
alloc/assign the rect, we could avoid the GC churn of making new
Rects only to drop them on the floor.
*/
Rect pad = new Rect();
// Special stuff for compatibility mode: if the target density is not
// the same as the display density, but the resource -is- the same as
// the display density, then don't scale it down to the target density.
// This allows us to load the system's density-correct resources into
// an application in compatibility mode, without scaling those down
// to the compatibility density only to have them scaled back up when
// drawn to the screen.
if (opts == null) opts = new BitmapFactory.Options();
opts.inScreenDensity = res != null
? res.getDisplayMetrics().noncompatDensityDpi : DisplayMetrics.DENSITY_DEVICE;
Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
if (bm != null) {
byte[] np = bm.getNinePatchChunk();
if (np == null || !NinePatch.isNinePatchChunk(np)) {
np = null;
pad = null;
}
final Rect opticalInsets = new Rect();
bm.getOpticalInsets(opticalInsets);
return drawableFromBitmap(res, bm, np, pad, opticalInsets, srcName);
}
return null;
}
/**
* Create a drawable from an XML document. For more information on how to
* create resources in XML, see
* <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
*/
public static Drawable createFromXml(Resources r, XmlPullParser parser)
throws XmlPullParserException, IOException {
return createFromXml(r, parser, null);
}
/**
* Create a drawable from an XML document using an optional {@link Theme}.
* For more information on how to create resources in XML, see
* <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
*/
public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme)
throws XmlPullParserException, IOException {
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
while ((type=parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty loop
}
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
Drawable drawable = createFromXmlInner(r, parser, attrs, theme);
if (drawable == null) {
throw new RuntimeException("Unknown initial tag: " + parser.getName());
}
return drawable;
}
/**
* Create from inside an XML document. Called on a parser positioned at
* a tag in an XML document, tries to create a Drawable from that tag.
* Returns null if the tag is not a valid drawable.
*/
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
return createFromXmlInner(r, parser, attrs, null);
}
/**
* Create a drawable from inside an XML document using an optional
* {@link Theme}. Called on a parser positioned at a tag in an XML
* document, tries to create a Drawable from that tag. Returns {@code null}
* if the tag is not a valid drawable.
*/
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
final Drawable drawable;
final String name = parser.getName();
switch (name) {
case "selector":
drawable = new StateListDrawable();
break;
case "animated-selector":
drawable = new AnimatedStateListDrawable();
break;
case "level-list":
drawable = new LevelListDrawable();
break;
case "layer-list":
drawable = new LayerDrawable();
break;
case "transition":
drawable = new TransitionDrawable();
break;
case "ripple":
drawable = new RippleDrawable();
break;
case "color":
drawable = new ColorDrawable();
break;
case "shape":
drawable = new GradientDrawable();
break;
case "vector":
drawable = new VectorDrawable();
break;
case "animated-vector":
drawable = new AnimatedVectorDrawable();
break;
case "scale":
drawable = new ScaleDrawable();
break;
case "clip":
drawable = new ClipDrawable();
break;
case "rotate":
drawable = new RotateDrawable();
break;
case "animated-rotate":
drawable = new AnimatedRotateDrawable();
break;
case "animation-list":
drawable = new AnimationDrawable();
break;
case "inset":
drawable = new InsetDrawable();
break;
case "bitmap":
drawable = new BitmapDrawable(r);
if (r != null) {
((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
}
break;
case "nine-patch":
drawable = new NinePatchDrawable();
if (r != null) {
((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
}
break;
default:
throw new XmlPullParserException(parser.getPositionDescription() +
": invalid drawable tag " + name);
}
drawable.inflate(r, parser, attrs, theme);
return drawable;
}
/**
* Create a drawable from file path name.
*/
public static Drawable createFromPath(String pathName) {
if (pathName == null) {
return null;
}
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
try {
Bitmap bm = BitmapFactory.decodeFile(pathName);
if (bm != null) {
return drawableFromBitmap(null, bm, null, null, null, pathName);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
return null;
}
/**
* Inflate this Drawable from an XML resource. Does not apply a theme.
*
* @see #inflate(Resources, XmlPullParser, AttributeSet, Theme)
*/
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
inflate(r, parser, attrs, null);
}
/**
* Inflate this Drawable from an XML resource optionally styled by a theme.
*
* @param r Resources used to resolve attribute values
* @param parser XML parser from which to inflate this Drawable
* @param attrs Base set of attribute values
* @param theme Theme to apply, may be null
* @throws XmlPullParserException
* @throws IOException
*/
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a;
if (theme != null) {
a = theme.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.Drawable, 0, 0);
} else {
a = r.obtainAttributes(attrs, com.android.internal.R.styleable.Drawable);
}
inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.Drawable_visible);
a.recycle();
}
/**
* Inflate a Drawable from an XML resource.
*
* @throws XmlPullParserException
* @throws IOException
*/
void inflateWithAttributes(Resources r, XmlPullParser parser, TypedArray attrs, int visibleAttr)
throws XmlPullParserException, IOException {
mVisible = attrs.getBoolean(visibleAttr, mVisible);
}
/**
* This abstract class is used by {@link Drawable}s to store shared constant state and data
* between Drawables. {@link BitmapDrawable}s created from the same resource will for instance
* share a unique bitmap stored in their ConstantState.
*
* <p>
* {@link #newDrawable(Resources)} can be used as a factory to create new Drawable instances
* from this ConstantState.
* </p>
*
* Use {@link Drawable#getConstantState()} to retrieve the ConstantState of a Drawable. Calling
* {@link Drawable#mutate()} on a Drawable should typically create a new ConstantState for that
* Drawable.
*/
public static abstract class ConstantState {
/**
* Create a new drawable without supplying resources the caller
* is running in. Note that using this means the density-dependent
* drawables (like bitmaps) will not be able to update their target
* density correctly. One should use {@link #newDrawable(Resources)}
* instead to provide a resource.
*/
public abstract Drawable newDrawable();
/**
* Create a new Drawable instance from its constant state. This
* must be implemented for drawables that change based on the target
* density of their caller (that is depending on whether it is
* in compatibility mode).
*/
public Drawable newDrawable(Resources res) {
return newDrawable();
}
/**
* Create a new Drawable instance from its constant state. This must be
* implemented for drawables that can have a theme applied.
*/
public Drawable newDrawable(Resources res, Theme theme) {
return newDrawable(null);
}
/**
* Return a bit mask of configuration changes that will impact
* this drawable (and thus require completely reloading it).
*/
public abstract int getChangingConfigurations();
/**
* @return Total pixel count
* @hide
*/
public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
return 0;
}
/** @hide */
protected final boolean isAtlasable(Bitmap bitmap) {
return bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888;
}
/**
* Return whether this constant state can have a theme applied.
*/
public boolean canApplyTheme() {
return false;
}
}
/**
* Return a {@link ConstantState} instance that holds the shared state of this Drawable.
*
* @return The ConstantState associated to that Drawable.
* @see ConstantState
* @see Drawable#mutate()
*/
public ConstantState getConstantState() {
return null;
}
private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,
Rect pad, Rect layoutBounds, String srcName) {
if (np != null) {
return new NinePatchDrawable(res, bm, np, pad, layoutBounds, srcName);
}
return new BitmapDrawable(res, bm);
}
/**
* Ensures the tint filter is consistent with the current tint color and
* mode.
*/
PorterDuffColorFilter updateTintFilter(PorterDuffColorFilter tintFilter, ColorStateList tint,
PorterDuff.Mode tintMode) {
if (tint == null || tintMode == null) {
return null;
}
final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
if (tintFilter == null) {
return new PorterDuffColorFilter(color, tintMode);
}
tintFilter.setColor(color);
tintFilter.setMode(tintMode);
return tintFilter;
}
/**
* Obtains styled attributes from the theme, if available, or unstyled
* resources if the theme is null.
*/
static TypedArray obtainAttributes(
Resources res, Theme theme, AttributeSet set, int[] attrs) {
if (theme == null) {
return res.obtainAttributes(set, attrs);
}
return theme.obtainStyledAttributes(set, attrs, 0, 0);
}
/**
* Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
* attribute's enum value.
*
* @hide
*/
public static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
switch (value) {
case 3: return Mode.SRC_OVER;
case 5: return Mode.SRC_IN;
case 9: return Mode.SRC_ATOP;
case 14: return Mode.MULTIPLY;
case 15: return Mode.SCREEN;
case 16: return Mode.ADD;
default: return defaultMode;
}
}
}