FileDocCategorySizeDatePackage
VectorDrawable.javaAPI DocAndroid 5.1 API56573Thu Mar 12 22:22:30 GMT 2015android.graphics.drawable

VectorDrawable

public 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 intapplyAlpha(int color, float alpha)

            int alphaBytes = Color.alpha(color);
            color &= 0x00FFFFFF;
            color |= ((int) (alphaBytes * alpha)) << 24;
            return color;
        
    public voidapplyTheme(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 booleancanApplyTheme()

            return (mVectorState != null && mVectorState.canApplyTheme()) || super.canApplyTheme();
        
    public voidclearMutated()

    hide

            super.clearMutated();
            mMutated = false;
        
    public static android.graphics.drawable.VectorDrawablecreate(android.content.res.Resources resources, int rid)

    hide

            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 voiddraw(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 intgetAlpha()

            return mVectorState.mVPathRenderer.getRootAlpha();
        
    public intgetChangingConfigurations()

            return super.getChangingConfigurations() | mVectorState.mChangingConfigurations;
        
    public ConstantStategetConstantState()

            mVectorState.mChangingConfigurations = getChangingConfigurations();
            return mVectorState;
        
    public intgetIntrinsicHeight()

            return (int) mVectorState.mVPathRenderer.mBaseHeight;
        
    public intgetIntrinsicWidth()

            return (int) mVectorState.mVPathRenderer.mBaseWidth;
        
    public intgetOpacity()

            return PixelFormat.TRANSLUCENT;
        
    public floatgetPixelSize()
    The size of a pixel when scaled from the intrinsic dimension to the viewport dimension. This is used to calculate the path animation accuracy.

    hide

            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.ObjectgetTargetByName(java.lang.String name)

            return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
        
    public voidinflate(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 voidinflateInternal(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 booleanisAutoMirrored()

            return mVectorState.mAutoMirrored;
        
    public booleanisStateful()

            return super.isStateful() || (mVectorState != null && mVectorState.mTint != null
                    && mVectorState.mTint.isStateful());
        
    public Drawablemutate()

            if (!mMutated && super.mutate() == this) {
                mVectorState = new VectorDrawableState(mVectorState);
                mMutated = true;
            }
            return this;
        
    private booleanneedMirroring()

            return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
        
    protected booleanonStateChange(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 voidprintGroupTree(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);
                }
            }
        
    voidsetAllowCaching(boolean allowCaching)

            mAllowCaching = allowCaching;
        
    public voidsetAlpha(int alpha)

            if (mVectorState.mVPathRenderer.getRootAlpha() != alpha) {
                mVectorState.mVPathRenderer.setRootAlpha(alpha);
                invalidateSelf();
            }
        
    public voidsetAutoMirrored(boolean mirrored)

            if (mVectorState.mAutoMirrored != mirrored) {
                mVectorState.mAutoMirrored = mirrored;
                invalidateSelf();
            }
        
    public voidsetColorFilter(android.graphics.ColorFilter colorFilter)

            mColorFilter = colorFilter;
            invalidateSelf();
        
    public voidsetTintList(android.content.res.ColorStateList tint)

            final VectorDrawableState state = mVectorState;
            if (state.mTint != tint) {
                state.mTint = tint;
                mTintFilter = updateTintFilter(mTintFilter, tint, state.mTintMode);
                invalidateSelf();
            }
        
    public voidsetTintMode(android.graphics.PorterDuff.Mode tintMode)

            final VectorDrawableState state = mVectorState;
            if (state.mTintMode != tintMode) {
                state.mTintMode = tintMode;
                mTintFilter = updateTintFilter(mTintFilter, state.mTint, tintMode);
                invalidateSelf();
            }
        
    private voidupdateStateFromTypedArray(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);
            }