VectorDrawablepublic class VectorDrawable extends Drawable This lets you create a drawable based on an XML vector graphic. It can be
defined in an XML file with the <vector> element.
The vector drawable has the following elements:
<vector>
- Used to defined a vector drawable
android:name
- Defines the name of this vector drawable.
android:width
- Used to defined the intrinsic width of the drawable.
This support all the dimension units, normally specified with dp.
android:height
- Used to defined the intrinsic height the drawable.
This support all the dimension units, normally specified with dp.
android:viewportWidth
- Used to defined the width of the viewport space. Viewport is basically
the virtual canvas where the paths are drawn on.
android:viewportHeight
- Used to defined the height of the viewport space. Viewport is basically
the virtual canvas where the paths are drawn on.
android:tint
- The color to apply to the drawable as a tint. By default, no tint is applied.
android:tintMode
- The Porter-Duff blending mode for the tint color. The default value is src_in.
android:autoMirrored
- Indicates if the drawable needs to be mirrored when its layout direction is
RTL (right-to-left).
android:alpha
- The opacity of this drawable.
<group>
- Defines a group of paths or subgroups, plus transformation information.
The transformations are defined in the same coordinates as the viewport.
And the transformations are applied in the order of scale, rotate then translate.
android:name
- Defines the name of the group.
android:rotation
- The degrees of rotation of the group.
android:pivotX
- The X coordinate of the pivot for the scale and rotation of the group.
This is defined in the viewport space.
android:pivotY
- The Y coordinate of the pivot for the scale and rotation of the group.
This is defined in the viewport space.
android:scaleX
- The amount of scale on the X Coordinate.
android:scaleY
- The amount of scale on the Y coordinate.
android:translateX
- The amount of translation on the X coordinate.
This is defined in the viewport space.
android:translateY
- The amount of translation on the Y coordinate.
This is defined in the viewport space.
<path>
- Defines paths to be drawn.
android:name
- Defines the name of the path.
android:pathData
- Defines path string. This is using exactly same format as "d" attribute
in the SVG's path data. This is defined in the viewport space.
android:fillColor
- Defines the color to fill the path (none if not present).
android:strokeColor
- Defines the color to draw the path outline (none if not present).
android:strokeWidth
- The width a path stroke.
android:strokeAlpha
- The opacity of a path stroke.
android:fillAlpha
- The opacity to fill the path with.
android:trimPathStart
- The fraction of the path to trim from the start, in the range from 0 to 1.
android:trimPathEnd
- The fraction of the path to trim from the end, in the range from 0 to 1.
android:trimPathOffset
- Shift trim region (allows showed region to include the start and end), in the range
from 0 to 1.
android:strokeLineCap
- Sets the linecap for a stroked path: butt, round, square.
android:strokeLineJoin
- Sets the lineJoin for a stroked path: miter,round,bevel.
android:strokeMiterLimit
- Sets the Miter limit for a stroked path.
<clip-path>
- Defines path to be the current clip.
android:name
- Defines the name of the clip path.
android:pathData
- Defines clip path string. This is using exactly same format as "d" attribute
in the SVG's path data.
Here is a simple VectorDrawable in this vectordrawable.xml file.
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="64dp"
android:width="64dp"
android:viewportHeight="600"
android:viewportWidth="600" >
<group
android:name="rotationGroup"
android:pivotX="300.0"
android:pivotY="300.0"
android:rotation="45.0" >
<path
android:name="v"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
</group>
</vector>
|
Fields Summary |
---|
private static final String | LOGTAG | private static final String | SHAPE_CLIP_PATH | private static final String | SHAPE_GROUP | private static final String | SHAPE_PATH | private static final String | SHAPE_VECTOR | private static final int | LINECAP_BUTT | private static final int | LINECAP_ROUND | private static final int | LINECAP_SQUARE | private static final int | LINEJOIN_MITER | private static final int | LINEJOIN_ROUND | private static final int | LINEJOIN_BEVEL | private static final boolean | DBG_VECTOR_DRAWABLE | private VectorDrawableState | mVectorState | private android.graphics.PorterDuffColorFilter | mTintFilter | private android.graphics.ColorFilter | mColorFilter | private boolean | mMutated | private boolean | mAllowCaching |
Constructors Summary |
---|
public VectorDrawable()
mVectorState = new VectorDrawableState();
| private VectorDrawable(VectorDrawableState state)
mVectorState = state;
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
|
Methods Summary |
---|
private static int | applyAlpha(int color, float alpha)
int alphaBytes = Color.alpha(color);
color &= 0x00FFFFFF;
color |= ((int) (alphaBytes * alpha)) << 24;
return color;
| public void | applyTheme(android.content.res.Resources.Theme t)
super.applyTheme(t);
final VectorDrawableState state = mVectorState;
if (state != null && state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.VectorDrawable);
try {
state.mCacheDirty = true;
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
}
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
}
final VPathRenderer path = state.mVPathRenderer;
if (path != null && path.canApplyTheme()) {
path.applyTheme(t);
}
| public boolean | canApplyTheme()
return (mVectorState != null && mVectorState.canApplyTheme()) || super.canApplyTheme();
| public void | clearMutated()
super.clearMutated();
mMutated = false;
| public static android.graphics.drawable.VectorDrawable | create(android.content.res.Resources resources, int rid)
try {
final XmlPullParser parser = resources.getXml(rid);
final 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");
}
final VectorDrawable drawable = new VectorDrawable();
drawable.inflate(resources, parser, attrs);
return drawable;
} catch (XmlPullParserException e) {
Log.e(LOGTAG, "parser error", e);
} catch (IOException e) {
Log.e(LOGTAG, "parser error", e);
}
return null;
| public void | draw(android.graphics.Canvas canvas)
final Rect bounds = getBounds();
if (bounds.width() == 0 || bounds.height() == 0) {
// too small to draw
return;
}
final int saveCount = canvas.save();
final boolean needMirroring = needMirroring();
canvas.translate(bounds.left, bounds.top);
if (needMirroring) {
canvas.translate(bounds.width(), 0);
canvas.scale(-1.0f, 1.0f);
}
// Color filters always override tint filters.
final ColorFilter colorFilter = mColorFilter == null ? mTintFilter : mColorFilter;
if (!mAllowCaching) {
// AnimatedVectorDrawable
if (!mVectorState.hasTranslucentRoot()) {
mVectorState.mVPathRenderer.draw(
canvas, bounds.width(), bounds.height(), colorFilter);
} else {
mVectorState.createCachedBitmapIfNeeded(bounds);
mVectorState.updateCachedBitmap(bounds);
mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter);
}
} else {
// Static Vector Drawable case.
mVectorState.createCachedBitmapIfNeeded(bounds);
if (!mVectorState.canReuseCache()) {
mVectorState.updateCachedBitmap(bounds);
mVectorState.updateCacheStates();
}
mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter);
}
canvas.restoreToCount(saveCount);
| public int | getAlpha()
return mVectorState.mVPathRenderer.getRootAlpha();
| public int | getChangingConfigurations()
return super.getChangingConfigurations() | mVectorState.mChangingConfigurations;
| public ConstantState | getConstantState()
mVectorState.mChangingConfigurations = getChangingConfigurations();
return mVectorState;
| public int | getIntrinsicHeight()
return (int) mVectorState.mVPathRenderer.mBaseHeight;
| public int | getIntrinsicWidth()
return (int) mVectorState.mVPathRenderer.mBaseWidth;
| public int | getOpacity()
return PixelFormat.TRANSLUCENT;
| public float | getPixelSize()The size of a pixel when scaled from the intrinsic dimension to the viewport dimension.
This is used to calculate the path animation accuracy.
if (mVectorState == null && mVectorState.mVPathRenderer == null ||
mVectorState.mVPathRenderer.mBaseWidth == 0 ||
mVectorState.mVPathRenderer.mBaseHeight == 0 ||
mVectorState.mVPathRenderer.mViewportHeight == 0 ||
mVectorState.mVPathRenderer.mViewportWidth == 0) {
return 1; // fall back to 1:1 pixel mapping.
}
float intrinsicWidth = mVectorState.mVPathRenderer.mBaseWidth;
float intrinsicHeight = mVectorState.mVPathRenderer.mBaseHeight;
float viewportWidth = mVectorState.mVPathRenderer.mViewportWidth;
float viewportHeight = mVectorState.mVPathRenderer.mViewportHeight;
float scaleX = viewportWidth / intrinsicWidth;
float scaleY = viewportHeight / intrinsicHeight;
return Math.min(scaleX, scaleY);
| java.lang.Object | getTargetByName(java.lang.String name)
return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
| public void | inflate(android.content.res.Resources res, org.xmlpull.v1.XmlPullParser parser, android.util.AttributeSet attrs, android.content.res.Resources.Theme theme)
final VectorDrawableState state = mVectorState;
final VPathRenderer pathRenderer = new VPathRenderer();
state.mVPathRenderer = pathRenderer;
final TypedArray a = obtainAttributes(res, theme, attrs, R.styleable.VectorDrawable);
updateStateFromTypedArray(a);
a.recycle();
state.mCacheDirty = true;
inflateInternal(res, parser, attrs, theme);
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
| private void | inflateInternal(android.content.res.Resources res, org.xmlpull.v1.XmlPullParser parser, android.util.AttributeSet attrs, android.content.res.Resources.Theme theme)
final VectorDrawableState state = mVectorState;
final VPathRenderer pathRenderer = state.mVPathRenderer;
boolean noPathTag = true;
// Use a stack to help to build the group tree.
// The top of the stack is always the current group.
final Stack<VGroup> groupStack = new Stack<VGroup>();
groupStack.push(pathRenderer.mRootGroup);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
final String tagName = parser.getName();
final VGroup currentGroup = groupStack.peek();
if (SHAPE_PATH.equals(tagName)) {
final VFullPath path = new VFullPath();
path.inflate(res, attrs, theme);
currentGroup.mChildren.add(path);
if (path.getPathName() != null) {
pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
}
noPathTag = false;
state.mChangingConfigurations |= path.mChangingConfigurations;
} else if (SHAPE_CLIP_PATH.equals(tagName)) {
final VClipPath path = new VClipPath();
path.inflate(res, attrs, theme);
currentGroup.mChildren.add(path);
if (path.getPathName() != null) {
pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
}
state.mChangingConfigurations |= path.mChangingConfigurations;
} else if (SHAPE_GROUP.equals(tagName)) {
VGroup newChildGroup = new VGroup();
newChildGroup.inflate(res, attrs, theme);
currentGroup.mChildren.add(newChildGroup);
groupStack.push(newChildGroup);
if (newChildGroup.getGroupName() != null) {
pathRenderer.mVGTargetsMap.put(newChildGroup.getGroupName(),
newChildGroup);
}
state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
}
} else if (eventType == XmlPullParser.END_TAG) {
final String tagName = parser.getName();
if (SHAPE_GROUP.equals(tagName)) {
groupStack.pop();
}
}
eventType = parser.next();
}
// Print the tree out for debug.
if (DBG_VECTOR_DRAWABLE) {
printGroupTree(pathRenderer.mRootGroup, 0);
}
if (noPathTag) {
final StringBuffer tag = new StringBuffer();
if (tag.length() > 0) {
tag.append(" or ");
}
tag.append(SHAPE_PATH);
throw new XmlPullParserException("no " + tag + " defined");
}
| public boolean | isAutoMirrored()
return mVectorState.mAutoMirrored;
| public boolean | isStateful()
return super.isStateful() || (mVectorState != null && mVectorState.mTint != null
&& mVectorState.mTint.isStateful());
| public Drawable | mutate()
if (!mMutated && super.mutate() == this) {
mVectorState = new VectorDrawableState(mVectorState);
mMutated = true;
}
return this;
| private boolean | needMirroring()
return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
| protected boolean | onStateChange(int[] stateSet)
final VectorDrawableState state = mVectorState;
if (state.mTint != null && state.mTintMode != null) {
mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
invalidateSelf();
return true;
}
return false;
| private void | printGroupTree(android.graphics.drawable.VectorDrawable$VGroup currentGroup, int level)
String indent = "";
for (int i = 0; i < level; i++) {
indent += " ";
}
// Print the current node
Log.v(LOGTAG, indent + "current group is :" + currentGroup.getGroupName()
+ " rotation is " + currentGroup.mRotate);
Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString());
// Then print all the children groups
for (int i = 0; i < currentGroup.mChildren.size(); i++) {
Object child = currentGroup.mChildren.get(i);
if (child instanceof VGroup) {
printGroupTree((VGroup) child, level + 1);
}
}
| void | setAllowCaching(boolean allowCaching)
mAllowCaching = allowCaching;
| public void | setAlpha(int alpha)
if (mVectorState.mVPathRenderer.getRootAlpha() != alpha) {
mVectorState.mVPathRenderer.setRootAlpha(alpha);
invalidateSelf();
}
| public void | setAutoMirrored(boolean mirrored)
if (mVectorState.mAutoMirrored != mirrored) {
mVectorState.mAutoMirrored = mirrored;
invalidateSelf();
}
| public void | setColorFilter(android.graphics.ColorFilter colorFilter)
mColorFilter = colorFilter;
invalidateSelf();
| public void | setTintList(android.content.res.ColorStateList tint)
final VectorDrawableState state = mVectorState;
if (state.mTint != tint) {
state.mTint = tint;
mTintFilter = updateTintFilter(mTintFilter, tint, state.mTintMode);
invalidateSelf();
}
| public void | setTintMode(android.graphics.PorterDuff.Mode tintMode)
final VectorDrawableState state = mVectorState;
if (state.mTintMode != tintMode) {
state.mTintMode = tintMode;
mTintFilter = updateTintFilter(mTintFilter, state.mTint, tintMode);
invalidateSelf();
}
| private void | updateStateFromTypedArray(android.content.res.TypedArray a)
final VectorDrawableState state = mVectorState;
final VPathRenderer pathRenderer = state.mVPathRenderer;
// Account for any configuration changes.
state.mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
if (tintMode != -1) {
state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
}
final ColorStateList tint = a.getColorStateList(R.styleable.VectorDrawable_tint);
if (tint != null) {
state.mTint = tint;
}
state.mAutoMirrored = a.getBoolean(
R.styleable.VectorDrawable_autoMirrored, state.mAutoMirrored);
pathRenderer.mViewportWidth = a.getFloat(
R.styleable.VectorDrawable_viewportWidth, pathRenderer.mViewportWidth);
pathRenderer.mViewportHeight = a.getFloat(
R.styleable.VectorDrawable_viewportHeight, pathRenderer.mViewportHeight);
if (pathRenderer.mViewportWidth <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
"<vector> tag requires viewportWidth > 0");
} else if (pathRenderer.mViewportHeight <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
"<vector> tag requires viewportHeight > 0");
}
pathRenderer.mBaseWidth = a.getDimension(
R.styleable.VectorDrawable_width, pathRenderer.mBaseWidth);
pathRenderer.mBaseHeight = a.getDimension(
R.styleable.VectorDrawable_height, pathRenderer.mBaseHeight);
if (pathRenderer.mBaseWidth <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
"<vector> tag requires width > 0");
} else if (pathRenderer.mBaseHeight <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
"<vector> tag requires height > 0");
}
final float alphaInFloat = a.getFloat(R.styleable.VectorDrawable_alpha,
pathRenderer.getAlpha());
pathRenderer.setAlpha(alphaInFloat);
final String name = a.getString(R.styleable.VectorDrawable_name);
if (name != null) {
pathRenderer.mRootName = name;
pathRenderer.mVGTargetsMap.put(name, pathRenderer);
}
|
|