美文网首页
不使用属性动画,通过反射设置Vector的属性

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

作者: Jerry2015 | 来源:发表于2016-07-08 23:24 被阅读152次

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

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

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

<code>
Drawable drawable = imageView.getDrawable();
if (drawable instanceof Animatable) {
((Animatable) drawable).start();
}
</code>

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

Paste_Image.png

这里debug看到drawable其实是一个AnimatedVectorDrawable(注意这里Api21以前是兼容包的类,这里因为我的手机是6.0所以这里是AnimatedVectorDrawable,其实原理完全一样);
跟进AnimatedVectorDrawable的start方法
<code>
@Override
public void start() {
ensureAnimatorSet();
// If any one of the animator has not ended, do nothing.
if (isStarted()) {
return;
}
mAnimatorSet.start();
invalidateSelf();
}
</code>
东西全出来了。看见mAnimatorSet.start();写过属性动画,应该会非常熟悉。跟进ensureAnimatorSet方法
<code>
private void ensureAnimatorSet() {
if (!mHasAnimatorSet) {
mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes);
mHasAnimatorSet = true;
mRes = null;
}
}
</code>
没什么好想的,继续跟进prepareLocalAnimators方法,
<code>
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);
}
}
}
</code>
这时候如果眼睛亮的话,应该会直接看见这一行
<code>
final Animator firstAnim = prepareLocalAnimator(0);
</code>
prepareLocalAnimator这里返回了一个Animator,跟进prepareLocalAnimator方法。
<code>
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;
}
</code>
一下子就豁然开朗了,
<code>
final Object target = mVectorDrawable.getTargetByName(targetName);
</code>
这一行IDE报了红线,表示这个方法不可访问。VectorDrawable里其实有这个方法
<code>
Object getTargetByName(String name) {
return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
}
</code>
好多层。我们跟到mVGTargetsMap那里看看那里有什么。可惜的是这个Map保存是Object类型,这时候我们可以继续debug watch这个Map查看里面的Object的运行时的实际类型,不过向下扫一眼看到了mVGTargetsMap所在的类叫VPathRenderer,这个类的构造方法是这个:
<code>
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);
}
}
</code>
倒数第三行put了this,这表示返回的target其实就是当前类。当前类保存在Map里,然后Map会通过
<code>
Object getTargetByName(String name) {
return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
}
</code>
返回作为Animator的tartget,这时候想到了什么?属性动画原理就是反射getset,那这个类非常可能就有mFillColor属性或者setFillColor方法。command+F12输入setfil

Paste_Image.png
好吧,终于发现你了。
<code>
@SuppressWarnings("unused")
void setFillColor(int fillColor) {
mFillColor = fillColor;
}
</code>
其实不光是fillColor其他的属性也全部都有。
<code>
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;
</code>
到这里剩下的应该就不用说了,思路基本已经有了,剩下的就简单了,无非就是敲键盘了。如果你不想敲也可以参考这里:
https://github.com/aesean/VectorHelper/blob/master/VectorHelper
保留作者,保留原始链接后,欢迎转载。原始链接:http://www.jianshu.com/p/2db6a5ce871b

相关文章

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

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

  • CALayer显示动画

    属性动画: 通过CABasicAnimation来设置属性动画 通过keyPath属性获取到属性的名字 通过fro...

  • 属性动画学习使用总结

    Android 属性动画总结 属性动画的优点 属性动画的使用步骤 实例化属性动画对象 设置属性动画的时长 启动属性...

  • Web学习笔记 - 第010天

    反射 通过反射可以设置类的私有属性 通过反射可以使用类的方法 文件上传 MySQL 文件类型为 longblob ...

  • Android属性动画基础篇

    1、什么是属性动画 简单来说就是通过改变对象属性而形成的动画效果。 2、为什么使用属性动画 我们知道,在属性动画之...

  • Juqery2

    jquery动画 : 通过animate方法可以设置元素某属性值上的动画,可以设置一个或多个属性值,动画执行完成后...

  • jquery动画

    jquery动画 通过animate方法可以设置元素某属性值上的动画,可以设置一个或多个属性值,动画执行完成后会执...

  • jQuery动画、事件

    jquery动画 通过animate方法可以设置元素某属性值上的动画,可以设置一个或多个属性值,动画执行完成后会执...

  • 2018-07-24 知识点

    jquery动画通过animate方法可以设置元素某属性值上的动画,可以设置一个或多个属性值,动画执行完成后会执行...

  • jQuery动画,事件

    jquery动画 通过animate方法可以设置元素某属性值上的动画,可以设置一个或多个属性值,动画执行完成后会执...

网友评论

      本文标题:不使用属性动画,通过反射设置Vector的属性

      本文链接:https://www.haomeiwen.com/subject/cierjttx.html