Add root alpha and animation to (Animated)VectorDrawable

b/17393626

Change-Id: If6a28b072f7d4bcb2b57022d86ec784f4c0d78f1
This commit is contained in:
ztenghui
2014-09-11 14:34:20 -07:00
parent 38c29ca3c7
commit 8490354dc6
6 changed files with 130 additions and 22 deletions

View File

@@ -5271,11 +5271,16 @@
<attr name="viewportWidth" format="float"/>
<!-- The height of the canvas the drawing is on. -->
<attr name="viewportHeight" format="float"/>
<!-- The name of this vector drawable -->
<attr name="name" />
<!-- The opacity of the whole vector drawable, as a value between 0
(completely transparent) and 1 (completely opaque). -->
<attr name="alpha" />
</declare-styleable>
<!-- Defines the group used in VectorDrawables. -->
<declare-styleable name="VectorDrawableGroup">
<!-- The Name of this group -->
<!-- The name of this group -->
<attr name="name" />
<!-- The amount to rotate the group -->
<attr name="rotation" />
@@ -5295,7 +5300,7 @@
<!-- Defines the path used in VectorDrawables. -->
<declare-styleable name="VectorDrawablePath">
<!-- The Name of this path -->
<!-- The name of this path -->
<attr name="name" />
<!-- The width a path stroke -->
<attr name="strokeWidth" format="float" />

View File

@@ -256,21 +256,22 @@ public class VectorDrawable extends Drawable {
}
if (!mAllowCaching) {
mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height());
// AnimatedVectorDrawable
if (!mVectorState.hasTranslucentRoot()) {
mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height());
} else {
mVectorState.createCachedBitmapIfNeeded(bounds);
mVectorState.updateCachedBitmap(bounds);
mVectorState.drawCachedBitmapWithRootAlpha(canvas);
}
} else {
Bitmap bitmap = mVectorState.mCachedBitmap;
if (bitmap == null || !mVectorState.canReuseCache(bounds.width(),
bounds.height())) {
bitmap = Bitmap.createBitmap(bounds.width(), bounds.height(),
Bitmap.Config.ARGB_8888);
Canvas tmpCanvas = new Canvas(bitmap);
mVectorState.mVPathRenderer.draw(tmpCanvas, bounds.width(), bounds.height());
mVectorState.mCachedBitmap = bitmap;
// Static Vector Drawable case.
mVectorState.createCachedBitmapIfNeeded(bounds);
if (!mVectorState.canReuseCache()) {
mVectorState.updateCachedBitmap(bounds);
mVectorState.updateCacheStates();
}
// The bitmap's size is the same as the bounds.
canvas.drawBitmap(bitmap, 0, 0, null);
mVectorState.drawCachedBitmapWithRootAlpha(canvas);
}
canvas.restoreToCount(saveCount);
@@ -492,6 +493,15 @@ public class VectorDrawable extends Drawable {
throw new XmlPullParserException(a.getPositionDescription() +
"<vector> tag requires height > 0");
}
final float alphaInFloat = a.getFloat(R.styleable.VectorDrawable_alpha,
pathRenderer.getAlpha());
pathRenderer.setAlpha(alphaInFloat);
pathRenderer.mRootName = a.getString(R.styleable.VectorDrawable_name);
if (pathRenderer.mRootName != null) {
pathRenderer.mVGTargetsMap.put(pathRenderer.mRootName, pathRenderer);
}
}
private void inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
@@ -646,15 +656,60 @@ public class VectorDrawable extends Drawable {
}
}
public boolean canReuseCache(int width, int height) {
// TODO: Support colorFilter here.
public void drawCachedBitmapWithRootAlpha(Canvas canvas) {
Paint alphaPaint = getRootAlphaPaint();
// The bitmap's size is the same as the bounds.
canvas.drawBitmap(mCachedBitmap, 0, 0, alphaPaint);
}
public boolean hasTranslucentRoot() {
return mVPathRenderer.getRootAlpha() < 255;
}
/**
* @return null when there is no need for alpha paint.
*/
public Paint getRootAlphaPaint() {
Paint paint = null;
boolean needsAlphaPaint = hasTranslucentRoot();
if (needsAlphaPaint) {
paint = new Paint();
paint.setAlpha(mVPathRenderer.getRootAlpha());
}
return paint;
}
public void updateCachedBitmap(Rect bounds) {
mCachedBitmap.eraseColor(Color.TRANSPARENT);
Canvas tmpCanvas = new Canvas(mCachedBitmap);
mVPathRenderer.draw(tmpCanvas, bounds.width(), bounds.height());
}
public void createCachedBitmapIfNeeded(Rect bounds) {
if (mCachedBitmap == null || !canReuseBitmap(bounds.width(),
bounds.height())) {
mCachedBitmap = Bitmap.createBitmap(bounds.width(), bounds.height(),
Bitmap.Config.ARGB_8888);
}
}
public boolean canReuseBitmap(int width, int height) {
if (width == mCachedBitmap.getWidth()
&& height == mCachedBitmap.getHeight()) {
return true;
}
return false;
}
public boolean canReuseCache() {
if (!mCacheDirty
&& mCachedThemeAttrs == mThemeAttrs
&& mCachedTint == mTint
&& mCachedTintMode == mTintMode
&& mCachedAutoMirrored == mAutoMirrored
&& width == mCachedBitmap.getWidth()
&& height == mCachedBitmap.getHeight()
&& mCachedRootAlpha == mVPathRenderer.getRootAlpha()) {
&& mCachedRootAlpha == mVPathRenderer.getRootAlpha()) {
return true;
}
return false;
@@ -729,7 +784,8 @@ public class VectorDrawable extends Drawable {
float mBaseHeight = 0;
float mViewportWidth = 0;
float mViewportHeight = 0;
private int mRootAlpha = 0xFF;
int mRootAlpha = 0xFF;
String mRootName = null;
final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>();
@@ -747,6 +803,17 @@ public class VectorDrawable extends Drawable {
return mRootAlpha;
}
// setAlpha() and getAlpha() are used mostly for animation purpose, since
// Animator like to use alpha from 0 to 1.
public void setAlpha(float alpha) {
setRootAlpha((int) (alpha * 255));
}
@SuppressWarnings("unused")
public float getAlpha() {
return getRootAlpha() / 255.0f;
}
public VPathRenderer(VPathRenderer copy) {
mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
mPath = new Path(copy.mPath);
@@ -757,6 +824,10 @@ public class VectorDrawable extends Drawable {
mViewportHeight = copy.mViewportHeight;
mChangingConfigurations = copy.mChangingConfigurations;
mRootAlpha = copy.mRootAlpha;
mRootName = copy.mRootName;
if (copy.mRootName != null) {
mVGTargetsMap.put(copy.mRootName, this);
}
}
public boolean canApplyTheme() {
@@ -856,7 +927,7 @@ public class VectorDrawable extends Drawable {
}
private void drawPath(VGroup vGroup, VPath vPath, Canvas canvas, int w, int h) {
final float scaleX = w / mViewportWidth;
final float scaleX = w / mViewportWidth;
final float scaleY = h / mViewportHeight;
final float minScale = Math.min(scaleX, scaleY);
@@ -1219,7 +1290,7 @@ public class VectorDrawable extends Drawable {
/**
* Clip path, which only has name and pathData.
*/
private static class VClipPath extends VPath{
private static class VClipPath extends VPath {
public VClipPath() {
// Empty constructor.
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="9000"
android:propertyName="alpha"
android:valueFrom="1.0"
android:valueTo="0.0"/>
</set>

View File

@@ -37,5 +37,8 @@
<target
android:name="rotationGroup"
android:animation="@anim/trim_path_animation04" />
<target
android:name="rootGroup"
android:animation="@anim/trim_path_animation06" />
</animated-vector>

View File

@@ -14,10 +14,12 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:name="rootGroup"
android:height="64dp"
android:width="64dp"
android:viewportHeight="600"
android:viewportWidth="600" >
android:viewportWidth="600"
android:alpha="0.5" >
<group
android:name="rotationGroup"

View File

@@ -31,6 +31,8 @@ public class AnimatedVectorDrawableTest extends Activity implements View.OnClick
R.drawable.animation_vector_progress_bar,
R.drawable.animation_vector_drawable_favorite,
R.drawable.animation_vector_drawable01,
// Duplicate to test constant state.
R.drawable.animation_vector_drawable01,
};
@Override