VectorDrawable.java revision 9cd14fc710cf81cc6d1c47d599abe349d3aa87dc
1e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/* 2e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Copyright (C) 2014 The Android Open Source Project 3e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 4e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * in compliance with the License. You may obtain a copy of the License at 6e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 7e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * http://www.apache.org/licenses/LICENSE-2.0 8e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 9e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Unless required by applicable law or agreed to in writing, software distributed under the License 10e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * or implied. See the License for the specific language governing permissions and limitations under 12e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * the License. 13e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 14e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 15e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapackage android.graphics.drawable; 16e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 17e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.res.ColorStateList; 18e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.res.Resources; 19e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.res.Resources.Theme; 20e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.res.TypedArray; 21e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Canvas; 22e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Color; 23e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.ColorFilter; 24e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Matrix; 25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Paint; 26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Path; 27e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.PathMeasure; 28e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.PixelFormat; 29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.PorterDuffColorFilter; 30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Rect; 31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Region; 32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.PorterDuff.Mode; 33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.ArrayMap; 34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.AttributeSet; 35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Log; 36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.PathParser; 37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Xml; 38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.internal.R; 40e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 41e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport org.xmlpull.v1.XmlPullParser; 42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport org.xmlpull.v1.XmlPullParserException; 43e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport org.xmlpull.v1.XmlPullParserFactory; 44e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 45e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.IOException; 46e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.util.ArrayList; 47e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.util.Arrays; 48e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.util.Stack; 49e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 50e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/** 51e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * This lets you create a drawable based on an XML vector graphic It can be 52e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * defined in an XML file with the <code><vector></code> element. 53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <p/> 54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * The vector drawable has the following elements: 55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <p/> 56e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dl> 57e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code><vector></code></dt> 58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Used to defined a vector drawable</dd> 59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code><size></code></dt> 60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Used to defined the intrinsic Width Height size of the drawable using 61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <code>android:width</code> and <code>android:height</code></dd> 62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code><viewport></code></dt> 63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Used to defined the size of the virtual canvas the paths are drawn on. 64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * The size is defined using the attributes <code>android:viewportHeight</code> 65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <code>android:viewportWidth</code></dd> 66e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code><group></code></dt> 67e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Defines a group of paths or subgroups, plus transformation information. 68e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * The transformations are defined in the same coordinates as the viewport. 69e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * And the transformations are applied in the order of scale, rotate then translate. </dd> 70e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:rotation</code> 71e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The degrees of rotation of the group.</dd></dt> 72e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:pivotX</code> 73e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The X coordinate of the pivot for the scale and rotation of the group</dd></dt> 74e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:pivotY</code> 75e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The Y coordinate of the pivot for the scale and rotation of the group</dd></dt> 76e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:scaleX</code> 77e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The amount of scale on the X Coordinate</dd></dt> 78e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:scaleY</code> 79e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The amount of scale on the Y coordinate</dd></dt> 80e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:translateX</code> 81e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The amount of translation on the X coordinate</dd></dt> 82e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:translateY</code> 83e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The amount of translation on the Y coordinate</dd></dt> 84e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code><path></code></dt> 85e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Defines paths to be drawn. 86e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dl> 87e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:name</code> 88e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Defines the name of the path.</dd></dt> 89e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:pathData</code> 90e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Defines path string. This is using exactly same format as "d" attribute 91e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * in the SVG's path data</dd></dt> 92e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:fill</code> 93e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Defines the color to fill the path (none if not present).</dd></dt> 94e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:stroke</code> 95e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Defines the color to draw the path outline (none if not present).</dd> 96e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * </dt> 97e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:strokeWidth</code> 98e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The width a path stroke</dd></dt> 99e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:strokeOpacity</code> 100e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The opacity of a path stroke</dd></dt> 101e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:fillOpacity</code> 102e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The opacity to fill the path with</dd></dt> 103e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:trimPathStart</code> 104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The fraction of the path to trim from the start from 0 to 1</dd></dt> 105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:trimPathEnd</code> 106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>The fraction of the path to trim from the end from 0 to 1</dd></dt> 107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:trimPathOffset</code> 108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Shift trim region (allows showed region to include the start and end) 109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * from 0 to 1</dd></dt> 110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:clipToPath</code> 111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Path will set the clip path</dd></dt> 112e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:strokeLineCap</code> 113e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Sets the linecap for a stroked path: butt, round, square</dd></dt> 114e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:strokeLineJoin</code> 115e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Sets the lineJoin for a stroked path: miter,round,bevel</dd></dt> 116e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dt><code>android:strokeMiterLimit</code> 117e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * <dd>Sets the Miter limit for a stroked path</dd></dt> 118e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * </dl> 119e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * </dd> 120e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 121e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapublic class VectorDrawable extends Drawable { 122e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final String LOGTAG = VectorDrawable.class.getSimpleName(); 123e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 124e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final String SHAPE_SIZE = "size"; 125e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final String SHAPE_VIEWPORT = "viewport"; 126e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final String SHAPE_GROUP = "group"; 127e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final String SHAPE_PATH = "path"; 128e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final String SHAPE_VECTOR = "vector"; 129e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 130e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int LINECAP_BUTT = 0; 131e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int LINECAP_ROUND = 1; 132e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int LINECAP_SQUARE = 2; 133e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 134e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int LINEJOIN_MITER = 0; 135e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int LINEJOIN_ROUND = 1; 136e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int LINEJOIN_BEVEL = 2; 137e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 138e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final boolean DBG_VECTOR_DRAWABLE = false; 139e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 140e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final VectorDrawableState mVectorState; 141e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 142e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>(); 143e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 144e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private PorterDuffColorFilter mTintFilter; 145e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 146e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public VectorDrawable() { 147e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState = new VectorDrawableState(null); 148e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 149e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 150e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private VectorDrawable(VectorDrawableState state, Resources res, Theme theme) { 151e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (theme != null && state.canApplyTheme()) { 152e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // If we need to apply a theme, implicitly mutate. 153e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState = new VectorDrawableState(state); 154e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka applyTheme(theme); 155e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 156e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState = state; 157e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 158e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 159e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); 160e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState.mVPathRenderer.setColorFilter(mTintFilter); 161e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 162e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 163e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Object getTargetByName(String name) { 164e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mVGTargetsMap.get(name); 165e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 166e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 167e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 168e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public ConstantState getConstantState() { 169e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mVectorState; 170e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 171e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 172e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 173e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void draw(Canvas canvas) { 174e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int saveCount = canvas.save(); 175e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final Rect bounds = getBounds(); 176e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.translate(bounds.left, bounds.top); 177e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height()); 178e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.restoreToCount(saveCount); 179e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 180e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 181e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 182e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getAlpha() { 183e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mVectorState.mVPathRenderer.getRootAlpha(); 184e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 185e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 186e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 187e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setAlpha(int alpha) { 188e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mVectorState.mVPathRenderer.getRootAlpha() != alpha) { 189e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState.mVPathRenderer.setRootAlpha(alpha); 190e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidateSelf(); 191e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 192e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 193e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 194e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 195e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setColorFilter(ColorFilter colorFilter) { 196e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VectorDrawableState state = mVectorState; 197e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (colorFilter != null) { 198e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Color filter overrides tint. 199e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTintFilter = null; 200e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (state.mTint != null && state.mTintMode != null) { 201e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Restore the tint filter, if we need one. 202e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int color = state.mTint.getColorForState(getState(), Color.TRANSPARENT); 203e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); 204e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka colorFilter = mTintFilter; 205e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 206e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 207e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka state.mVPathRenderer.setColorFilter(colorFilter); 208e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidateSelf(); 209e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 210e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 211e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 212e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setTint(ColorStateList tint, Mode tintMode) { 213e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VectorDrawableState state = mVectorState; 214e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (state.mTint != tint || state.mTintMode != tintMode) { 215e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka state.mTint = tint; 216e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka state.mTintMode = tintMode; 217e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 218e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTintFilter = updateTintFilter(mTintFilter, tint, tintMode); 219e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState.mVPathRenderer.setColorFilter(mTintFilter); 220e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidateSelf(); 221e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 222e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 223e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 224e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 225e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected boolean onStateChange(int[] stateSet) { 226e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VectorDrawableState state = mVectorState; 227e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (state.mTint != null && state.mTintMode != null) { 228e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); 229e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState.mVPathRenderer.setColorFilter(mTintFilter); 230e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 231e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 232e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 233e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 234e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 235e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 236e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getOpacity() { 237e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return PixelFormat.TRANSLUCENT; 238e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 239e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 240e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /** 241e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Sets padding for this shape, defined by a Rect object. Define the padding 242e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * in the Rect object as: left, top, right, bottom. 243e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 244e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setPadding(Rect padding) { 245e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka setPadding(padding.left, padding.top, padding.right, padding.bottom); 246e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 247e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 248e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /** 249e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Sets padding for the shape. 250e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 251e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * @param left padding for the left side (in pixels) 252e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * @param top padding for the top (in pixels) 253e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * @param right padding for the right side (in pixels) 254e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * @param bottom padding for the bottom (in pixels) 255e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 256e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setPadding(int left, int top, int right, int bottom) { 257e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if ((left | top | right | bottom) == 0) { 258e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState.mPadding = null; 259e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 260e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mVectorState.mPadding == null) { 261e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState.mPadding = new Rect(); 262e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 263e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVectorState.mPadding.set(left, top, right, bottom); 264e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 265e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidateSelf(); 266e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 267e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 268e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 269e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getIntrinsicWidth() { 270e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return (int) mVectorState.mVPathRenderer.mBaseWidth; 271e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 272e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 273e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 274e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getIntrinsicHeight() { 275e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return (int) mVectorState.mVPathRenderer.mBaseHeight; 276e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 277e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 278e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 279e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean getPadding(Rect padding) { 280e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mVectorState.mPadding != null) { 281e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka padding.set(mVectorState.mPadding); 282e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 283e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 284e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return super.getPadding(padding); 285e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 286e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 287e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 288e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 289e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean canApplyTheme() { 290e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return super.canApplyTheme() || mVectorState != null && mVectorState.canApplyTheme(); 291e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 292e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 293e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 294e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void applyTheme(Theme t) { 295e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka super.applyTheme(t); 296e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 297e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VectorDrawableState state = mVectorState; 298e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VPathRenderer path = state.mVPathRenderer; 299e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (path != null && path.canApplyTheme()) { 300e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka path.applyTheme(t); 301e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 302e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 303e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 304e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /** @hide */ 305e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static VectorDrawable create(Resources resources, int rid) { 306e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 307e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final XmlPullParser xpp = resources.getXml(rid); 308e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final AttributeSet attrs = Xml.asAttributeSet(xpp); 309e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 310e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka factory.setNamespaceAware(true); 311e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 312e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VectorDrawable drawable = new VectorDrawable(); 313e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka drawable.inflate(resources, xpp, attrs); 314e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 315e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return drawable; 316e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (XmlPullParserException e) { 317e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.e(LOGTAG, "parser error", e); 318e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (IOException e) { 319e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.e(LOGTAG, "parser error", e); 320e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 321e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return null; 322e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 323e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 324e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static int applyAlpha(int color, float alpha) { 325e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int alphaBytes = Color.alpha(color); 326e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka color &= 0x00FFFFFF; 327e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka color |= ((int) (alphaBytes * alpha)) << 24; 328e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return color; 329e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 330e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 331e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 332e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 333e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) 334e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka throws XmlPullParserException, IOException { 335e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final TypedArray a = obtainAttributes(res, theme, attrs,R.styleable.VectorDrawable); 336e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateStateFromTypedArray(a); 337e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka a.recycle(); 338e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 339e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VectorDrawableState state = mVectorState; 340e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka state.mVPathRenderer = inflateInternal(res, parser, attrs, theme); 341e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 342e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); 343e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka state.mVPathRenderer.setColorFilter(mTintFilter); 344e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 345e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 346e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void updateStateFromTypedArray(TypedArray a) { 347e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VectorDrawableState state = mVectorState; 348e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 349e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Extract the theme attributes, if any. 350e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka state.mThemeAttrs = a.extractThemeAttrs(); 351e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 352e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1); 353e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tintMode != -1) { 354e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN); 355e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 356e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 357e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final ColorStateList tint = a.getColorStateList(R.styleable.VectorDrawable_tint); 358e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tint != null) { 359e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka state.mTint = tint; 360e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 361e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 362e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 363e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) 364e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka throws XmlPullParserException, IOException { 365e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VPathRenderer pathRenderer = new VPathRenderer(); 366e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 367e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean noSizeTag = true; 368e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean noViewportTag = true; 369e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean noPathTag = true; 370e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 371e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Use a stack to help to build the group tree. 372e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // The top of the stack is always the current group. 373e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final Stack<VGroup> groupStack = new Stack<VGroup>(); 374e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka groupStack.push(pathRenderer.mRootGroup); 375e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 376e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int eventType = parser.getEventType(); 377e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (eventType != XmlPullParser.END_DOCUMENT) { 378e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (eventType == XmlPullParser.START_TAG) { 379e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final String tagName = parser.getName(); 380e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VGroup currentGroup = groupStack.peek(); 381e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 382e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (SHAPE_PATH.equals(tagName)) { 383e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VPath path = new VPath(); 384e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka path.inflate(res, attrs, theme); 385e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka currentGroup.add(path); 386e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (path.getPathName() != null) { 387e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVGTargetsMap.put(path.getPathName(), path); 388e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 389e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka noPathTag = false; 390e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (SHAPE_SIZE.equals(tagName)) { 391e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka pathRenderer.parseSize(res, attrs); 392e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka noSizeTag = false; 393e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (SHAPE_VIEWPORT.equals(tagName)) { 394e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka pathRenderer.parseViewport(res, attrs); 395e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka noViewportTag = false; 396e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (SHAPE_GROUP.equals(tagName)) { 397e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka VGroup newChildGroup = new VGroup(); 398e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka newChildGroup.inflate(res, attrs, theme); 399e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka currentGroup.mChildGroupList.add(newChildGroup); 400e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka groupStack.push(newChildGroup); 401e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (newChildGroup.getGroupName() != null) { 402e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVGTargetsMap.put(newChildGroup.getGroupName(), newChildGroup); 403e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 404e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 405e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (eventType == XmlPullParser.END_TAG) { 406e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final String tagName = parser.getName(); 407e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (SHAPE_GROUP.equals(tagName)) { 408e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka groupStack.pop(); 409e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 410e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 411e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka eventType = parser.next(); 412e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 413e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 414e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Print the tree out for debug. 415e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (DBG_VECTOR_DRAWABLE) { 416e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka printGroupTree(pathRenderer.mRootGroup, 0); 417e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 418e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 419e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (noSizeTag || noViewportTag || noPathTag) { 420e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final StringBuffer tag = new StringBuffer(); 421e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 422e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (noSizeTag) { 423e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tag.append(SHAPE_SIZE); 424e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 425e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 426e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (noViewportTag) { 427e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tag.length() > 0) { 428e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tag.append(" & "); 429e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 430e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tag.append(SHAPE_SIZE); 431e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 432e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 433e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (noPathTag) { 434e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tag.length() > 0) { 435e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tag.append(" or "); 436e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 437e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tag.append(SHAPE_PATH); 438e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 439e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 440e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka throw new XmlPullParserException("no " + tag + " defined"); 441e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 442e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 443e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return pathRenderer; 444e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 445e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 446e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void printGroupTree(VGroup currentGroup, int level) { 447e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka String indent = ""; 448e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0 ; i < level ; i++) { 449e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka indent += " "; 450e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 451e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Print the current node 452e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.v(LOGTAG, indent + "current group is :" + currentGroup.getGroupName() 453e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka + " rotation is " + currentGroup.mRotate); 454e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString()); 455e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Then print all the children 456e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0 ; i < currentGroup.mChildGroupList.size(); i++) { 457e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka printGroupTree(currentGroup.mChildGroupList.get(i), level + 1); 458e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 459e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 460e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 461e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static class VectorDrawableState extends ConstantState { 462e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int[] mThemeAttrs; 463e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int mChangingConfigurations; 464e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka VPathRenderer mVPathRenderer; 465e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Rect mPadding; 466e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ColorStateList mTint; 467e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Mode mTintMode; 468e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 469e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public VectorDrawableState(VectorDrawableState copy) { 470e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (copy != null) { 471e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mThemeAttrs = copy.mThemeAttrs; 472e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mChangingConfigurations = copy.mChangingConfigurations; 473e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // TODO: Make sure the constant state are handled correctly. 474e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mVPathRenderer = new VPathRenderer(copy.mVPathRenderer); 475e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPadding = new Rect(copy.mPadding); 476e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTint = copy.mTint; 477e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTintMode = copy.mTintMode; 478e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 479e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 480e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 481e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 482e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Drawable newDrawable() { 483e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new VectorDrawable(this, null, null); 484e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 485e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 486e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 487e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Drawable newDrawable(Resources res) { 488e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new VectorDrawable(this, res, null); 489e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 490e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 491e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 492e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Drawable newDrawable(Resources res, Theme theme) { 493e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new VectorDrawable(this, res, theme); 494e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 495e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 496e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 497e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getChangingConfigurations() { 498e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mChangingConfigurations; 499e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 500e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 501e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 502e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static class VPathRenderer { 503e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /* Right now the internal data structure is organized as a tree. 504e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Each node can be a group node, or a path. 505e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * A group node can have groups or paths as children, but a path node has 506e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * no children. 507e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * One example can be: 508e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Root Group 509e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * / | \ 510e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Group Path Group 511e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * / \ | 512e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Path Path Path 513e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 514e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 515e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final VGroup mRootGroup; 516e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 517e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final Path mPath = new Path(); 518e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final Path mRenderPath = new Path(); 519e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final Matrix IDENTITY_MATRIX = new Matrix(); 520e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 521e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private Paint mStrokePaint; 522e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private Paint mFillPaint; 523e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private ColorFilter mColorFilter; 524e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private PathMeasure mPathMeasure; 525e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 526e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mBaseWidth = 0; 527e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mBaseHeight = 0; 528e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mViewportWidth = 0; 529e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mViewportHeight = 0; 530e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int mRootAlpha = 0xFF; 531e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 532e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final Matrix mFinalPathMatrix = new Matrix(); 533e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 534e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public VPathRenderer() { 535e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRootGroup = new VGroup(); 536e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 537e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 538e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setRootAlpha(int alpha) { 539e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRootAlpha = alpha; 540e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 541e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 542e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getRootAlpha() { 543e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mRootAlpha; 544e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 545e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 546e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public VPathRenderer(VPathRenderer copy) { 547e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRootGroup = copy.mRootGroup; 548e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mBaseWidth = copy.mBaseWidth; 549e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mBaseHeight = copy.mBaseHeight; 550e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mViewportWidth = copy.mViewportHeight; 551e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mViewportHeight = copy.mViewportHeight; 552e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 553e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 554e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean canApplyTheme() { 555e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // If one of the paths can apply theme, then return true; 556e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return recursiveCanApplyTheme(mRootGroup); 557e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 558e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 559e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private boolean recursiveCanApplyTheme(VGroup currentGroup) { 560e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We can do a tree traverse here, if there is one path return true, 561e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // then we return true for the whole tree. 562e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final ArrayList<VPath> paths = currentGroup.mPathList; 563e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int j = paths.size() - 1; j >= 0; j--) { 564e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VPath path = paths.get(j); 565e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (path.canApplyTheme()) { 566e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 567e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 568e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 569e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 570e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final ArrayList<VGroup> childGroups = currentGroup.mChildGroupList; 571e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 572e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0; i < childGroups.size(); i++) { 573e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka VGroup childGroup = childGroups.get(i); 574e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (childGroup.canApplyTheme() 575e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka || recursiveCanApplyTheme(childGroup)) { 576e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 577e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 578e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 579e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 580e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 581e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 582e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void applyTheme(Theme t) { 583e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Apply theme to every path of the tree. 584e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka recursiveApplyTheme(mRootGroup, t); 585e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 586e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 587e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void recursiveApplyTheme(VGroup currentGroup, Theme t) { 588e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We can do a tree traverse here, apply theme to all paths which 589e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // can apply theme. 590e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final ArrayList<VPath> paths = currentGroup.mPathList; 591e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int j = paths.size() - 1; j >= 0; j--) { 592e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final VPath path = paths.get(j); 593e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (path.canApplyTheme()) { 594e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka path.applyTheme(t); 595e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 596e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 597e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 598e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final ArrayList<VGroup> childGroups = currentGroup.mChildGroupList; 599e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 600e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0; i < childGroups.size(); i++) { 601e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka VGroup childGroup = childGroups.get(i); 602e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (childGroup.canApplyTheme()) { 603e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka childGroup.applyTheme(t); 604e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 605e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka recursiveApplyTheme(childGroup, t); 606e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 607e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 608e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 609e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 610e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setColorFilter(ColorFilter colorFilter) { 611e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mColorFilter = colorFilter; 612e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 613e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mFillPaint != null) { 614e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFillPaint.setColorFilter(colorFilter); 615e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 616e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 617e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mStrokePaint != null) { 618e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mStrokePaint.setColorFilter(colorFilter); 619e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 620e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 621e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 622e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 623e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void drawGroupTree(VGroup currentGroup, Matrix currentMatrix, 624e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float currentAlpha, Canvas canvas, int w, int h) { 625e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Calculate current group's matrix by preConcat the parent's and 626e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // and the current one on the top of the stack. 627e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Basically the Mfinal = Mviewport * M0 * M1 * M2; 628e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Mi the local matrix at level i of the group tree. 629e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka currentGroup.mStackedMatrix.set(currentMatrix); 630e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 631e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix); 632e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 633e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float stackedAlpha = currentAlpha * currentGroup.mGroupAlpha; 634e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka drawPath(currentGroup, stackedAlpha, canvas, w, h); 635e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Draw the group tree in post order. 636e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0 ; i < currentGroup.mChildGroupList.size(); i++) { 637e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka drawGroupTree(currentGroup.mChildGroupList.get(i), 638e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka currentGroup.mStackedMatrix, stackedAlpha, canvas, w, h); 639e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 640e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 641e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 642e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void draw(Canvas canvas, int w, int h) { 643e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Travese the tree in pre-order to draw. 644e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka drawGroupTree(mRootGroup, IDENTITY_MATRIX, ((float) mRootAlpha) / 0xFF, canvas, w, h); 645e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 646e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 647e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void drawPath(VGroup vGroup, float stackedAlpha, Canvas canvas, int w, int h) { 648e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float scale = Math.min(h / mViewportHeight, w / mViewportWidth); 649e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 650e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFinalPathMatrix.set(vGroup.mStackedMatrix); 651e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFinalPathMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f); 652e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFinalPathMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f); 653e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 654e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ArrayList<VPath> paths = vGroup.getPaths(); 655e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0; i < paths.size(); i++) { 656e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka VPath vPath = paths.get(i); 657e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka vPath.toPath(mPath); 658e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final Path path = mPath; 659e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 660e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (vPath.mTrimPathStart != 0.0f || vPath.mTrimPathEnd != 1.0f) { 661e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float start = (vPath.mTrimPathStart + vPath.mTrimPathOffset) % 1.0f; 662e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float end = (vPath.mTrimPathEnd + vPath.mTrimPathOffset) % 1.0f; 663e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 664e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mPathMeasure == null) { 665e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPathMeasure = new PathMeasure(); 666e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 667e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPathMeasure.setPath(mPath, false); 668e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 669e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float len = mPathMeasure.getLength(); 670e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka start = start * len; 671e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka end = end * len; 672e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka path.reset(); 673e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (start > end) { 674e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPathMeasure.getSegment(start, len, path, true); 675e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPathMeasure.getSegment(0f, end, path, true); 676e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 677e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPathMeasure.getSegment(start, end, path, true); 678e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 679e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka path.rLineTo(0, 0); // fix bug in measure 680e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 681e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 682e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderPath.reset(); 683e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 684e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderPath.addPath(path, mFinalPathMatrix); 685e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 686e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (vPath.mClip) { 687e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.clipPath(mRenderPath, Region.Op.REPLACE); 688e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 689e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (vPath.mFillColor != 0) { 690e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mFillPaint == null) { 691e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFillPaint = new Paint(); 692e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFillPaint.setColorFilter(mColorFilter); 693e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFillPaint.setStyle(Paint.Style.FILL); 694e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFillPaint.setAntiAlias(true); 695e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 696e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFillPaint.setColor(applyAlpha(vPath.mFillColor, stackedAlpha)); 697e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.drawPath(mRenderPath, mFillPaint); 698e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 699e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 700e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (vPath.mStrokeColor != 0) { 701e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mStrokePaint == null) { 702e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mStrokePaint = new Paint(); 703e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mStrokePaint.setColorFilter(mColorFilter); 704e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mStrokePaint.setStyle(Paint.Style.STROKE); 705e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mStrokePaint.setAntiAlias(true); 706e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 707e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 708e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final Paint strokePaint = mStrokePaint; 709e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (vPath.mStrokeLineJoin != null) { 710e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka strokePaint.setStrokeJoin(vPath.mStrokeLineJoin); 711e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 712e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 713e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (vPath.mStrokeLineCap != null) { 714e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka strokePaint.setStrokeCap(vPath.mStrokeLineCap); 715e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 716e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 717e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka strokePaint.setStrokeMiter(vPath.mStrokeMiterlimit * scale); 718e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 719e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka strokePaint.setColor(applyAlpha(vPath.mStrokeColor, stackedAlpha)); 720e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka strokePaint.setStrokeWidth(vPath.mStrokeWidth * scale); 721e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.drawPath(mRenderPath, strokePaint); 722e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 723e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 724e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 725e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 726e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 727e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void parseViewport(Resources r, AttributeSet attrs) 728e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka throws XmlPullParserException { 729e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableViewport); 730e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mViewportWidth = a.getFloat(R.styleable.VectorDrawableViewport_viewportWidth, mViewportWidth); 731e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mViewportHeight = a.getFloat(R.styleable.VectorDrawableViewport_viewportHeight, mViewportHeight); 732e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 733e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mViewportWidth <= 0) { 734e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka throw new XmlPullParserException(a.getPositionDescription() + 735e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka "<viewport> tag requires viewportWidth > 0"); 736e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (mViewportHeight <= 0) { 737e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka throw new XmlPullParserException(a.getPositionDescription() + 738e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka "<viewport> tag requires viewportHeight > 0"); 739e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 740e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 741e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka a.recycle(); 742e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 743e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 744e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void parseSize(Resources r, AttributeSet attrs) 745e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka throws XmlPullParserException { 746e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableSize); 747e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mBaseWidth = a.getDimension(R.styleable.VectorDrawableSize_width, mBaseWidth); 748e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mBaseHeight = a.getDimension(R.styleable.VectorDrawableSize_height, mBaseHeight); 749e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 750e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mBaseWidth <= 0) { 751e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka throw new XmlPullParserException(a.getPositionDescription() + 752e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka "<size> tag requires width > 0"); 753e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (mBaseHeight <= 0) { 754e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka throw new XmlPullParserException(a.getPositionDescription() + 755e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka "<size> tag requires height > 0"); 756e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 757e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 758e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka a.recycle(); 759e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 760e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 761e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 762e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 763e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka static class VGroup { 764e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final ArrayList<VPath> mPathList = new ArrayList<VPath>(); 765e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final ArrayList<VGroup> mChildGroupList = new ArrayList<VGroup>(); 766e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 767e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mRotate = 0; 768e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mPivotX = 0; 769e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mPivotY = 0; 770e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mScaleX = 1; 771e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mScaleY = 1; 772e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mTranslateX = 0; 773e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mTranslateY = 0; 774e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mGroupAlpha = 1; 775e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 776e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // mLocalMatrix is parsed from the XML. 777e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final Matrix mLocalMatrix = new Matrix(); 778e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // mStackedMatrix is only used when drawing, it combines all the 779e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // parents' local matrices with the current one. 780e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final Matrix mStackedMatrix = new Matrix(); 781e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 782e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int[] mThemeAttrs; 783e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 784e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private String mGroupName = null; 785e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 786e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /* Getter and Setter */ 787e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public float getRotation() { 788e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mRotate; 789e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 790e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 791e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setRotation(float rotation) { 792e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (rotation != mRotate) { 793e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRotate = rotation; 794e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateLocalMatrix(); 795e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 796e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 797e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 798e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public float getPivotX() { 799e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mPivotX; 800e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 801e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 802e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setPivotX(float pivotX) { 803e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (pivotX != mPivotX) { 804e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPivotX = pivotX; 805e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateLocalMatrix(); 806e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 807e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 808e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 809e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public float getPivotY() { 810e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mPivotY; 811e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 812e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 813e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setPivotY(float pivotY) { 814e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (pivotY != mPivotY) { 815e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPivotY = pivotY; 816e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateLocalMatrix(); 817e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 818e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 819e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 820e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public float getScaleX() { 821e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mScaleX; 822e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 823e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 824e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setScaleX(float scaleX) { 825e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (scaleX != mScaleX) { 826e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mScaleX = scaleX; 827e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateLocalMatrix(); 828e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 829e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 830e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 831e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public float getScaleY() { 832e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mScaleY; 833e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 834e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 835e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setScaleY(float scaleY) { 836e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (scaleY != mScaleY) { 837e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mScaleY = scaleY; 838e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateLocalMatrix(); 839e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 840e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 841e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 842e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public float getTranslateX() { 843e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mTranslateX; 844e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 845e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 846e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setTranslateX(float translateX) { 847e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (translateX != mTranslateX) { 848e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTranslateX = translateX; 849e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateLocalMatrix(); 850e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 851e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 852e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 853e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public float getTranslateY() { 854e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mTranslateY; 855e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 856e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 857e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setTranslateY(float translateY) { 858e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (translateY != mTranslateY) { 859e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTranslateY = translateY; 860e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateLocalMatrix(); 861e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 862e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 863e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 864e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public float getAlpha() { 865e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mGroupAlpha; 866e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 867e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 868e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setAlpha(float groupAlpha) { 869e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (groupAlpha != mGroupAlpha) { 870e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mGroupAlpha = groupAlpha; 871e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 872e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 873e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 874e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public String getGroupName() { 875e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mGroupName; 876e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 877e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 878e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Matrix getLocalMatrix() { 879e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mLocalMatrix; 880e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 881e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 882e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void add(VPath path) { 883e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPathList.add(path); 884e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 885e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 886e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean canApplyTheme() { 887e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mThemeAttrs != null; 888e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 889e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 890e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void applyTheme(Theme t) { 891e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mThemeAttrs == null) { 892e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return; 893e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 894e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 895e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final TypedArray a = t.resolveAttributes( 896e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mThemeAttrs, R.styleable.VectorDrawablePath); 897e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 898e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate); 899e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX); 900e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, mPivotY); 901e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mScaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX, mScaleX); 902e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY); 903e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX); 904e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY); 905e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha); 906e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateLocalMatrix(); 907e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (a.hasValue(R.styleable.VectorDrawableGroup_name)) { 908e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mGroupName = a.getString(R.styleable.VectorDrawableGroup_name); 909e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 910e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka a.recycle(); 911e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 912e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 913e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void inflate(Resources res, AttributeSet attrs, Theme theme) { 914e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final TypedArray a = obtainAttributes(res, theme, attrs, R.styleable.VectorDrawableGroup); 915e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int[] themeAttrs = a.extractThemeAttrs(); 916e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 917e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mThemeAttrs = themeAttrs; 918e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // NOTE: The set of attributes loaded here MUST match the 919e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // set of attributes loaded in applyTheme. 920e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 921e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_rotation] == 0) { 922e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate); 923e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 924e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 925e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_pivotX] == 0) { 926e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX); 927e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 928e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 929e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_pivotY] == 0) { 930e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, mPivotY); 931e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 932e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 933e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_scaleX] == 0) { 934e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mScaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX, mScaleX); 935e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 936e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 937e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_scaleY] == 0) { 938e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY); 939e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 940e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 941e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_translateX] == 0) { 942e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX); 943e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 944e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 945e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_translateY] == 0) { 946e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY); 947e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 948e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 949e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_name] == 0) { 950e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mGroupName = a.getString(R.styleable.VectorDrawableGroup_name); 951e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 952e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 953e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_alpha] == 0) { 954e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha); 955e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 956e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 957e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateLocalMatrix(); 958e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka a.recycle(); 959e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 960e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 961e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void updateLocalMatrix() { 962e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // The order we apply is the same as the 963e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // RenderNode.cpp::applyViewPropertyTransforms(). 964e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLocalMatrix.reset(); 965e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLocalMatrix.postTranslate(-mPivotX, -mPivotY); 966e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLocalMatrix.postScale(mScaleX, mScaleY); 967e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLocalMatrix.postRotate(mRotate, 0, 0); 968e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY); 969e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 970e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 971e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /** 972e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Must return in order of adding 973e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * @return ordered list of paths 974e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 975e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public ArrayList<VPath> getPaths() { 976e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mPathList; 977e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 978e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 979e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 980e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 981e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static class VPath { 982e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int[] mThemeAttrs; 983e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 984e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int mStrokeColor = 0; 985e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float mStrokeWidth = 0; 986e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float mStrokeOpacity = Float.NaN; 987e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int mFillColor = Color.BLACK; 988e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int mFillRule; 989e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float mFillOpacity = Float.NaN; 990e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float mTrimPathStart = 0; 991e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float mTrimPathEnd = 1; 992e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float mTrimPathOffset = 0; 993e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 994e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean mClip = false; 995e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Paint.Cap mStrokeLineCap = Paint.Cap.BUTT; 996e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Paint.Join mStrokeLineJoin = Paint.Join.MITER; 997e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float mStrokeMiterlimit = 4; 998e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 999e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private PathParser.PathDataNode[] mNode = null; 1000e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private String mPathName; 1001e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 1002e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public VPath() { 1003e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Empty constructor. 1004e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 1005e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 1006e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void toPath(Path path) { 1007e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka path.reset(); 1008e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mNode != null) { 1009e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka PathParser.PathDataNode.nodesToPath(mNode, path); 1010 } 1011 } 1012 1013 public String getPathName() { 1014 return mPathName; 1015 } 1016 1017 private Paint.Cap getStrokeLineCap(int id, Paint.Cap defValue) { 1018 switch (id) { 1019 case LINECAP_BUTT: 1020 return Paint.Cap.BUTT; 1021 case LINECAP_ROUND: 1022 return Paint.Cap.ROUND; 1023 case LINECAP_SQUARE: 1024 return Paint.Cap.SQUARE; 1025 default: 1026 return defValue; 1027 } 1028 } 1029 1030 private Paint.Join getStrokeLineJoin(int id, Paint.Join defValue) { 1031 switch (id) { 1032 case LINEJOIN_MITER: 1033 return Paint.Join.MITER; 1034 case LINEJOIN_ROUND: 1035 return Paint.Join.ROUND; 1036 case LINEJOIN_BEVEL: 1037 return Paint.Join.BEVEL; 1038 default: 1039 return defValue; 1040 } 1041 } 1042 1043 /* Setters and Getters */ 1044 int getStroke() { 1045 return mStrokeColor; 1046 } 1047 1048 void setStroke(int strokeColor) { 1049 mStrokeColor = strokeColor; 1050 } 1051 1052 float getStrokeWidth() { 1053 return mStrokeWidth; 1054 } 1055 1056 void setStrokeWidth(float strokeWidth) { 1057 mStrokeWidth = strokeWidth; 1058 } 1059 1060 float getStrokeOpacity() { 1061 return mStrokeOpacity; 1062 } 1063 1064 void setStrokeOpacity(float strokeOpacity) { 1065 mStrokeOpacity = strokeOpacity; 1066 } 1067 1068 int getFill() { 1069 return mFillColor; 1070 } 1071 1072 void setFill(int fillColor) { 1073 mFillColor = fillColor; 1074 } 1075 1076 float getFillOpacity() { 1077 return mFillOpacity; 1078 } 1079 1080 void setFillOpacity(float fillOpacity) { 1081 mFillOpacity = fillOpacity; 1082 } 1083 1084 float getTrimPathStart() { 1085 return mTrimPathStart; 1086 } 1087 1088 void setTrimPathStart(float trimPathStart) { 1089 mTrimPathStart = trimPathStart; 1090 } 1091 1092 float getTrimPathEnd() { 1093 return mTrimPathEnd; 1094 } 1095 1096 void setTrimPathEnd(float trimPathEnd) { 1097 mTrimPathEnd = trimPathEnd; 1098 } 1099 1100 float getTrimPathOffset() { 1101 return mTrimPathOffset; 1102 } 1103 1104 void setTrimPathOffset(float trimPathOffset) { 1105 mTrimPathOffset = trimPathOffset; 1106 } 1107 1108 public void inflate(Resources r, AttributeSet attrs, Theme theme) { 1109 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawablePath); 1110 final int[] themeAttrs = a.extractThemeAttrs(); 1111 mThemeAttrs = themeAttrs; 1112 1113 // NOTE: The set of attributes loaded here MUST match the 1114 // set of attributes loaded in applyTheme. 1115 if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_clipToPath] == 0) { 1116 mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, mClip); 1117 } 1118 1119 if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_name] == 0) { 1120 mPathName = a.getString(R.styleable.VectorDrawablePath_name); 1121 } 1122 1123 if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pathData] == 0) { 1124 mNode = PathParser.createNodesFromPathData(a.getString( 1125 R.styleable.VectorDrawablePath_pathData)); 1126 } 1127 1128 if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_fill] == 0) { 1129 mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, mFillColor); 1130 } 1131 1132 if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_fillOpacity] == 0) { 1133 mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, mFillOpacity); 1134 } 1135 1136 if (themeAttrs == null 1137 || themeAttrs[R.styleable.VectorDrawablePath_strokeLineCap] == 0) { 1138 mStrokeLineCap = getStrokeLineCap( 1139 a.getInt(R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap); 1140 } 1141 1142 if (themeAttrs == null 1143 || themeAttrs[R.styleable.VectorDrawablePath_strokeLineJoin] == 0) { 1144 mStrokeLineJoin = getStrokeLineJoin( 1145 a.getInt(R.styleable.VectorDrawablePath_strokeLineJoin, -1), mStrokeLineJoin); 1146 } 1147 1148 if (themeAttrs == null 1149 || themeAttrs[R.styleable.VectorDrawablePath_strokeMiterLimit] == 0) { 1150 mStrokeMiterlimit = a.getFloat( 1151 R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit); 1152 } 1153 1154 if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_stroke] == 0) { 1155 mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_stroke, mStrokeColor); 1156 } 1157 1158 if (themeAttrs == null 1159 || themeAttrs[R.styleable.VectorDrawablePath_strokeOpacity] == 0) { 1160 mStrokeOpacity = a.getFloat( 1161 R.styleable.VectorDrawablePath_strokeOpacity, mStrokeOpacity); 1162 } 1163 1164 if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_strokeWidth] == 0) { 1165 mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, mStrokeWidth); 1166 } 1167 1168 if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_trimPathEnd] == 0) { 1169 mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, mTrimPathEnd); 1170 } 1171 1172 if (themeAttrs == null 1173 || themeAttrs[R.styleable.VectorDrawablePath_trimPathOffset] == 0) { 1174 mTrimPathOffset = a.getFloat( 1175 R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset); 1176 } 1177 1178 if (themeAttrs == null 1179 || themeAttrs[R.styleable.VectorDrawablePath_trimPathStart] == 0) { 1180 mTrimPathStart = a.getFloat( 1181 R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart); 1182 } 1183 1184 updateColorAlphas(); 1185 1186 a.recycle(); 1187 } 1188 1189 public boolean canApplyTheme() { 1190 return mThemeAttrs != null; 1191 } 1192 1193 public void applyTheme(Theme t) { 1194 if (mThemeAttrs == null) { 1195 return; 1196 } 1197 1198 final TypedArray a = t.resolveAttributes( 1199 mThemeAttrs, R.styleable.VectorDrawablePath); 1200 1201 mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, mClip); 1202 1203 if (a.hasValue(R.styleable.VectorDrawablePath_name)) { 1204 mPathName = a.getString(R.styleable.VectorDrawablePath_name); 1205 } 1206 1207 if (a.hasValue(R.styleable.VectorDrawablePath_pathData)) { 1208 mNode = PathParser.createNodesFromPathData(a.getString( 1209 R.styleable.VectorDrawablePath_pathData)); 1210 } 1211 1212 mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, mFillColor); 1213 mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, mFillOpacity); 1214 1215 mStrokeLineCap = getStrokeLineCap(a.getInt( 1216 R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap); 1217 mStrokeLineJoin = getStrokeLineJoin(a.getInt( 1218 R.styleable.VectorDrawablePath_strokeLineJoin, -1), mStrokeLineJoin); 1219 mStrokeMiterlimit = a.getFloat( 1220 R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit); 1221 mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_stroke, mStrokeColor); 1222 mStrokeOpacity = a.getFloat( 1223 R.styleable.VectorDrawablePath_strokeOpacity, mStrokeOpacity); 1224 mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, mStrokeWidth); 1225 1226 mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, mTrimPathEnd); 1227 mTrimPathOffset = a.getFloat( 1228 R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset); 1229 mTrimPathStart = a.getFloat( 1230 R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart); 1231 1232 updateColorAlphas(); 1233 a.recycle(); 1234 } 1235 1236 private void updateColorAlphas() { 1237 if (!Float.isNaN(mFillOpacity)) { 1238 mFillColor = applyAlpha(mFillColor, mFillOpacity); 1239 } 1240 1241 if (!Float.isNaN(mStrokeOpacity)) { 1242 mStrokeColor = applyAlpha(mStrokeColor, mStrokeOpacity); 1243 } 1244 } 1245 } 1246} 1247