diff options
author | Alan Viverette <alanv@google.com> | 2015-09-22 15:03:50 -0400 |
---|---|---|
committer | Alan Viverette <alanv@google.com> | 2015-09-22 15:03:50 -0400 |
commit | 50c29cd92f4fc410f61e2d14ce77f5e8e76daf0b (patch) | |
tree | d74fa575f18c92b25539fdcf04f1cddb8e05d808 | |
parent | 31cb4bb41fe8290a46e6b7660eb1deeb67ee1558 (diff) |
Allow color state list for vector drawable fill and stroke
Removes unnecessary invalidateSelf() from VD.onStateChange(). This is
handled by the view hosting the drawable.
Bug: 22984152
Change-Id: Idf11a0ffef392cb1d8452aa3f5f836b35027a756
3 files changed, 175 insertions, 36 deletions
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 174722538014..1105ca4b6cbc 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -126,9 +126,13 @@ import java.util.Stack; * <dd>Defines path data using exactly same format as "d" attribute * in the SVG's path data. This is defined in the viewport space.</dd> * <dt><code>android:fillColor</code></dt> - * <dd>Defines the color to fill the path (none if not present).</dd> + * <dd>Specifies the color used to fill the path. May be a color or (SDK 24+ only) a color state + * list. If this property is animated, any value set by the animation will override the original + * value. No path fill is drawn if this property is not specified.</dd> * <dt><code>android:strokeColor</code></dt> - * <dd>Defines the color to draw the path outline (none if not present).</dd> + * <dd>Specifies the color used to draw the path outline. May be a color or (SDK 24+ only) a color + * state list. If this property is animated, any value set by the animation will override the + * original value. No path outline is drawn if this property is not specified.</dd> * <dt><code>android:strokeWidth</code></dt> * <dd>The width a path stroke.</dd> * <dt><code>android:strokeAlpha</code></dt> @@ -374,19 +378,24 @@ public class VectorDrawable extends Drawable { @Override public boolean isStateful() { - return super.isStateful() || (mVectorState != null && mVectorState.mTint != null - && mVectorState.mTint.isStateful()); + return super.isStateful() || (mVectorState != null && mVectorState.isStateful()); } @Override protected boolean onStateChange(int[] stateSet) { + boolean changed = false; + final VectorDrawableState state = mVectorState; + if (state.mVPathRenderer != null && state.mVPathRenderer.onStateChange(stateSet)) { + changed = true; + state.mCacheDirty = true; + } if (state.mTint != null && state.mTintMode != null) { mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); - invalidateSelf(); - return true; + changed = true; } - return false; + + return changed; } @Override @@ -664,7 +673,7 @@ public class VectorDrawable extends Drawable { if (SHAPE_PATH.equals(tagName)) { final VFullPath path = new VFullPath(); path.inflate(res, attrs, theme); - currentGroup.mChildren.add(path); + currentGroup.addChild(path); if (path.getPathName() != null) { pathRenderer.mVGTargetsMap.put(path.getPathName(), path); } @@ -673,7 +682,7 @@ public class VectorDrawable extends Drawable { } else if (SHAPE_CLIP_PATH.equals(tagName)) { final VClipPath path = new VClipPath(); path.inflate(res, attrs, theme); - currentGroup.mChildren.add(path); + currentGroup.addChild(path); if (path.getPathName() != null) { pathRenderer.mVGTargetsMap.put(path.getPathName(), path); } @@ -681,7 +690,7 @@ public class VectorDrawable extends Drawable { } else if (SHAPE_GROUP.equals(tagName)) { VGroup newChildGroup = new VGroup(); newChildGroup.inflate(res, attrs, theme); - currentGroup.mChildren.add(newChildGroup); + currentGroup.addChild(newChildGroup); groupStack.push(newChildGroup); if (newChildGroup.getGroupName() != null) { pathRenderer.mVGTargetsMap.put(newChildGroup.getGroupName(), @@ -700,7 +709,7 @@ public class VectorDrawable extends Drawable { // Print the tree out for debug. if (DBG_VECTOR_DRAWABLE) { - printGroupTree(pathRenderer.mRootGroup, 0); + pathRenderer.printGroupTree(); } if (noPathTag) { @@ -715,24 +724,6 @@ public class VectorDrawable extends Drawable { } } - private void printGroupTree(VGroup currentGroup, int level) { - String indent = ""; - for (int i = 0; i < level; i++) { - indent += " "; - } - // Print the current node - Log.v(LOGTAG, indent + "current group is :" + currentGroup.getGroupName() - + " rotation is " + currentGroup.mRotate); - Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString()); - // Then print all the children groups - for (int i = 0; i < currentGroup.mChildren.size(); i++) { - final VObject child = currentGroup.mChildren.get(i); - if (child instanceof VGroup) { - printGroupTree((VGroup) child, level + 1); - } - } - } - @Override public int getChangingConfigurations() { return super.getChangingConfigurations() | mVectorState.getChangingConfigurations(); @@ -890,6 +881,11 @@ public class VectorDrawable extends Drawable { return mChangingConfigurations | (mTint != null ? mTint.getChangingConfigurations() : 0); } + + public boolean isStateful() { + return (mTint != null && mTint.isStateful()) + || (mVPathRenderer != null && mVPathRenderer.isStateful()); + } } private static class VPathRenderer { @@ -972,21 +968,35 @@ public class VectorDrawable extends Drawable { mRootGroup.applyTheme(t); } + public boolean onStateChange(int[] stateSet) { + return mRootGroup.onStateChange(stateSet); + } + + public boolean isStateful() { + return mRootGroup.isStateful(); + } + public void draw(Canvas canvas, int w, int h, ColorFilter filter) { final float scaleX = w / mViewportWidth; final float scaleY = h / mViewportHeight; mRootGroup.draw(canvas, mTempState, Matrix.IDENTITY_MATRIX, filter, scaleX, scaleY); } + + public void printGroupTree() { + mRootGroup.printGroupTree(""); + } } private static class VGroup implements VObject { + private static final String GROUP_INDENT = " "; + // mStackedMatrix is only used temporarily when drawing, it combines all // the parents' local matrices with the current one. private final Matrix mStackedMatrix = new Matrix(); ///////////////////////////////////////////////////// // Variables below need to be copied (deep copy if applicable) for mutation. - final ArrayList<VObject> mChildren = new ArrayList<>(); + private final ArrayList<VObject> mChildren = new ArrayList<>(); private float mRotate = 0; private float mPivotX = 0; @@ -995,6 +1005,7 @@ public class VectorDrawable extends Drawable { private float mScaleY = 1; private float mTranslateX = 0; private float mTranslateY = 0; + private boolean mIsStateful; // mLocalMatrix is updated based on the update of transformation information, // either parsed from the XML or by animation. @@ -1011,6 +1022,7 @@ public class VectorDrawable extends Drawable { mScaleY = copy.mScaleY; mTranslateX = copy.mTranslateX; mTranslateY = copy.mTranslateY; + mIsStateful = copy.mIsStateful; mThemeAttrs = copy.mThemeAttrs; mGroupName = copy.mGroupName; mChangingConfigurations = copy.mChangingConfigurations; @@ -1054,6 +1066,12 @@ public class VectorDrawable extends Drawable { return mLocalMatrix; } + public void addChild(VObject child) { + mChildren.add(child); + + mIsStateful |= child.isStateful(); + } + @Override public void draw(Canvas canvas, TempState temp, Matrix currentMatrix, ColorFilter filter, float scaleX, float scaleY) { @@ -1109,6 +1127,26 @@ public class VectorDrawable extends Drawable { } @Override + public boolean onStateChange(int[] stateSet) { + boolean changed = false; + + final ArrayList<VObject> children = mChildren; + for (int i = 0, count = children.size(); i < count; i++) { + final VObject child = children.get(i); + if (child.isStateful()) { + changed |= child.onStateChange(stateSet); + } + } + + return changed; + } + + @Override + public boolean isStateful() { + return mIsStateful; + } + + @Override public boolean canApplyTheme() { if (mThemeAttrs != null) { return true; @@ -1139,6 +1177,9 @@ public class VectorDrawable extends Drawable { final VObject child = children.get(i); if (child.canApplyTheme()) { child.applyTheme(t); + + // Applying a theme may have made the child stateful. + mIsStateful |= child.isStateful(); } } } @@ -1153,6 +1194,24 @@ public class VectorDrawable extends Drawable { mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY); } + public void printGroupTree(String indent) { + Log.v(LOGTAG, indent + "group:" + getGroupName() + " rotation is " + mRotate); + Log.v(LOGTAG, indent + "matrix:" + getLocalMatrix().toString()); + + final int count = mChildren.size(); + if (count > 0) { + indent += GROUP_INDENT; + } + + // Then print all the children groups. + for (int i = 0; i < count; i++) { + final VObject child = mChildren.get(i); + if (child instanceof VGroup) { + ((VGroup) child).printGroupTree(indent); + } + } + } + /* Setters and Getters, used by animator from AnimatedVectorDrawable. */ @SuppressWarnings("unused") public float getRotation() { @@ -1398,6 +1457,16 @@ public class VectorDrawable extends Drawable { // No-op. } + @Override + public boolean onStateChange(int[] stateSet) { + return false; + } + + @Override + public boolean isStateful() { + return false; + } + private void updateStateFromTypedArray(TypedArray a) { // Account for any configuration changes. mChangingConfigurations |= a.getChangingConfigurations(); @@ -1427,9 +1496,11 @@ public class VectorDrawable extends Drawable { // Variables below need to be copied (deep copy if applicable) for mutation. private int[] mThemeAttrs; + ColorStateList mStrokeColors = null; int mStrokeColor = Color.TRANSPARENT; float mStrokeWidth = 0; + ColorStateList mFillColors = null; int mFillColor = Color.TRANSPARENT; float mStrokeAlpha = 1.0f; int mFillRule; @@ -1448,11 +1519,14 @@ public class VectorDrawable extends Drawable { public VFullPath(VFullPath copy) { super(copy); + mThemeAttrs = copy.mThemeAttrs; + mStrokeColors = copy.mStrokeColors; mStrokeColor = copy.mStrokeColor; mStrokeWidth = copy.mStrokeWidth; mStrokeAlpha = copy.mStrokeAlpha; + mFillColors = copy.mFillColors; mFillColor = copy.mFillColor; mFillRule = copy.mFillRule; mFillAlpha = copy.mFillAlpha; @@ -1492,6 +1566,31 @@ public class VectorDrawable extends Drawable { } @Override + public boolean onStateChange(int[] stateSet) { + boolean changed = false; + + if (mStrokeColors != null && mStrokeColors.isStateful()) { + final int strokeColor = mStrokeColor; + mStrokeColor = mStrokeColors.getColorForState(stateSet, strokeColor); + changed |= strokeColor != mStrokeColor; + } + + if (mFillColors != null && mFillColors.isStateful()) { + final int fillColor = mFillColor; + mFillColor = mFillColors.getColorForState(stateSet, fillColor); + changed |= fillColor != mFillColor; + } + + return changed; + } + + @Override + public boolean isStateful() { + return mStrokeColors != null && mStrokeColors.isStateful() + || mFillColors != null && mFillColors.isStateful(); + } + + @Override public void toPath(TempState temp, Path path) { super.toPath(temp, path); @@ -1614,8 +1713,20 @@ public class VectorDrawable extends Drawable { mNodes = PathParser.createNodesFromPathData(pathData); } - mFillColor = a.getColor(R.styleable.VectorDrawablePath_fillColor, - mFillColor); + final ColorStateList fillColors = a.getColorStateList( + R.styleable.VectorDrawablePath_fillColor); + if (fillColors != null) { + mFillColors = fillColors; + mFillColor = fillColors.getDefaultColor(); + } + + final ColorStateList strokeColors = a.getColorStateList( + R.styleable.VectorDrawablePath_strokeColor); + if (strokeColors != null) { + mStrokeColors = strokeColors; + mStrokeColor = strokeColors.getDefaultColor(); + } + mFillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha, mFillAlpha); mStrokeLineCap = getStrokeLineCap(a.getInt( @@ -1624,8 +1735,6 @@ public class VectorDrawable extends Drawable { R.styleable.VectorDrawablePath_strokeLineJoin, -1), mStrokeLineJoin); mStrokeMiterlimit = a.getFloat( R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit); - mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_strokeColor, - mStrokeColor); mStrokeAlpha = a.getFloat(R.styleable.VectorDrawablePath_strokeAlpha, mStrokeAlpha); mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, @@ -1662,6 +1771,7 @@ public class VectorDrawable extends Drawable { @SuppressWarnings("unused") void setStrokeColor(int strokeColor) { + mStrokeColors = null; mStrokeColor = strokeColor; } @@ -1692,6 +1802,7 @@ public class VectorDrawable extends Drawable { @SuppressWarnings("unused") void setFillColor(int fillColor) { + mFillColors = null; mFillColor = fillColor; } @@ -1752,5 +1863,7 @@ public class VectorDrawable extends Drawable { void inflate(Resources r, AttributeSet attrs, Theme theme); boolean canApplyTheme(); void applyTheme(Theme t); + boolean onStateChange(int[] state); + boolean isStateful(); } } diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml index 705cc34ff62b..2be99bed4bfb 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml @@ -21,10 +21,14 @@ <group> <path + android:name="box0" + android:pathData="m0,0l480,0l0,480l-480,0l0-480z" + android:fillColor="@android:color/white" /> + <path android:name="box1" android:pathData="m20,200l100,90l180-180l-35-35l-145,145l-60-60l-40,40z" - android:fillColor="?android:attr/colorControlActivated" - android:strokeColor="?android:attr/colorControlActivated" + android:fillColor="?android:attr/colorControlNormal" + android:strokeColor="?android:attr/colorControlNormal" android:strokeLineCap="round" android:strokeLineJoin="round" /> </group> diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java index a23d81933749..85fc452add3e 100644 --- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java +++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java @@ -18,7 +18,12 @@ import android.graphics.drawable.VectorDrawable; import android.os.Bundle; import android.util.Log; import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.GridLayout; @SuppressWarnings({"UnusedDeclaration"}) @@ -55,6 +60,23 @@ public class VectorDrawable01 extends Activity { container.setBackgroundColor(0xFF888888); final Button []bArray = new Button[icon.length]; + CheckBox toggle = new CheckBox(this); + toggle.setText("Toggle"); + toggle.setChecked(true); + toggle.setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + ViewGroup vg = (ViewGroup) buttonView.getParent(); + for (int i = 0, count = vg.getChildCount(); i < count; i++) { + View child = vg.getChildAt(i); + if (child != buttonView) { + child.setEnabled(isChecked); + } + } + } + }); + container.addView(toggle); + for (int i = 0; i < icon.length; i++) { Button button = new Button(this); bArray[i] = button; |