不使用属性动画,通过反射设置Vector的属性

最近看到一篇文章介绍Vector兼容方案的。参考http://www.jianshu.com/p/e3614e7abc03

尝试写了下Demo感觉还不错,但是遇到一个问题,需要Vector的颜色需要动态调整,前面的文章里通过属性动画一步步是可以实现这个效果的,但我只是改个颜色又是要写xml又是要用动画,这有点大炮打蚊子了,而且动画持续时间设置多少算合适?属性动画肯定不是优雅的解决方案。属性动画无非就是反射,既然属性动画能修改的,那我们自己通过反射肯定也是可以修改的。
这篇文章就是介绍下如何避免使用属性动画,来修改VectorDrawable的属性。其实整个过程就是查找需要反射的属性路径的过程,只要知道什么是反射,基本没什么难度。

既然是反射,那怎么反射呢?既然有源码,跟进源码看看AnimatedVectorDrawable具体是如何操作的。


Drawable drawable = imageView.getDrawable();
if (drawable instanceof Animatable) {
((Animatable) drawable).start();
}

上面是前面文章给的开启Vector动画的代码。我们跟进start看看。很不幸是个接口。这时候怎么办?直接debug watch drawable的真实类,或者打印日志drawable.getClass().toString()看下真实的类。

不使用属性动画,通过反射设置Vector的属性_第1张图片
Paste_Image.png

这里debug看到drawable其实是一个AnimatedVectorDrawable(注意这里Api21以前是兼容包的类,这里因为我的手机是6.0所以这里是AnimatedVectorDrawable,其实原理完全一样);
跟进AnimatedVectorDrawable的start方法

@Override
public void start() {
ensureAnimatorSet();
// If any one of the animator has not ended, do nothing.
if (isStarted()) {
return;
}
mAnimatorSet.start();
invalidateSelf();
}

东西全出来了。看见mAnimatorSet.start();写过属性动画,应该会非常熟悉。跟进ensureAnimatorSet方法

private void ensureAnimatorSet() {
if (!mHasAnimatorSet) {
mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes);
mHasAnimatorSet = true;
mRes = null;
}
}

没什么好想的,继续跟进prepareLocalAnimators方法,

public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet,
@Nullable Resources res) {
// Check for uninflated animators. We can remove this after we add
// support for Animator.applyTheme(). See comments in inflate().
if (mPendingAnims != null) {
// Attempt to load animators without applying a theme.
if (res != null) {
inflatePendingAnimators(res, null);
} else {
Log.e(LOGTAG, "Failed to load animators. Either the AnimatedVectorDrawable" + " must be created using a Resources object or applyTheme() must be" + " called with a non-null Theme object.");
}
mPendingAnims = null;
}
// Perform a deep copy of the constant state's animators.
final int count = mAnimators == null ? 0 : mAnimators.size();
if (count > 0) {
final Animator firstAnim = prepareLocalAnimator(0);
final AnimatorSet.Builder builder = animatorSet.play(firstAnim);
for (int i = 1; i < count; ++i) {
final Animator nextAnim = prepareLocalAnimator(i);
builder.with(nextAnim);
}
}
}

这时候如果眼睛亮的话,应该会直接看见这一行

final Animator firstAnim = prepareLocalAnimator(0);

prepareLocalAnimator这里返回了一个Animator,跟进prepareLocalAnimator方法。

private Animator prepareLocalAnimator(int index) {
final Animator animator = mAnimators.get(index);
final Animator localAnimator = animator.clone();
final String targetName = mTargetNameMap.get(animator);
final Object target = mVectorDrawable.getTargetByName(targetName);
localAnimator.setTarget(target);
return localAnimator;
}

一下子就豁然开朗了,

final Object target = mVectorDrawable.getTargetByName(targetName);

这一行IDE报了红线,表示这个方法不可访问。VectorDrawable里其实有这个方法

Object getTargetByName(String name) {
return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
}

好多层。我们跟到mVGTargetsMap那里看看那里有什么。可惜的是这个Map保存是Object类型,这时候我们可以继续debug watch这个Map查看里面的Object的运行时的实际类型,不过向下扫一眼看到了mVGTargetsMap所在的类叫VPathRenderer,这个类的构造方法是这个:

public VPathRenderer(VPathRenderer copy) {
mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
mPath = new Path(copy.mPath);
mRenderPath = new Path(copy.mRenderPath);
mBaseWidth = copy.mBaseWidth;
mBaseHeight = copy.mBaseHeight;
mViewportWidth = copy.mViewportWidth;
mViewportHeight = copy.mViewportHeight;
mOpticalInsets = copy.mOpticalInsets;
mChangingConfigurations = copy.mChangingConfigurations;
mRootAlpha = copy.mRootAlpha;
mRootName = copy.mRootName;
mTargetDensity = copy.mTargetDensity;
if (copy.mRootName != null) {
mVGTargetsMap.put(copy.mRootName, this);
}
}

倒数第三行put了this,这表示返回的target其实就是当前类。当前类保存在Map里,然后Map会通过

Object getTargetByName(String name) {
return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
}

返回作为Animator的tartget,这时候想到了什么?属性动画原理就是反射getset,那这个类非常可能就有mFillColor属性或者setFillColor方法。command+F12输入setfil

不使用属性动画,通过反射设置Vector的属性_第2张图片
Paste_Image.png

好吧,终于发现你了。

@SuppressWarnings("unused")
void setFillColor(int fillColor) {
mFillColor = fillColor;
}

其实不光是fillColor其他的属性也全部都有。

int mStrokeColor = Color.TRANSPARENT;
float mStrokeWidth = 0;
int mFillColor = Color.TRANSPARENT;
float mStrokeAlpha = 1.0f;
int mFillRule;
float mFillAlpha = 1.0f;
float mTrimPathStart = 0;
float mTrimPathEnd = 1;
float mTrimPathOffset = 0;
Paint.Cap mStrokeLineCap = Paint.Cap.BUTT;
Paint.Join mStrokeLineJoin = Paint.Join.MITER;
float mStrokeMiterlimit = 4;

到这里剩下的应该就不用说了,思路基本已经有了,剩下的就简单了,无非就是敲键盘了。如果你不想敲也可以参考这里:
https://github.com/aesean/VectorHelper/blob/master/VectorHelper
保留作者,保留原始链接后,欢迎转载。原始链接: http://www.jianshu.com/p/2db6a5ce871b

你可能感兴趣的:(不使用属性动画,通过反射设置Vector的属性)