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

最近看到一篇文章介绍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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    未聞椛洺阅读 2,740评论 0 10
  • 属性动画的集成关系Paste_Image.png 看一段属性动画的使用代码 跟进ObjectAnimator.of...
    明朗__阅读 7,478评论 0 2
  • Animation Animation类是所有动画(scale、alpha、translate、rotate)的基...
    四月一号阅读 1,934评论 0 10
  • 学习力这东西也真的需要灵感!除了日常生活中已经习惯的各种学习力,想创新,还得要宝宝接受,真的很不容易! 今天么,宝...
    幽香_5126阅读 165评论 0 0
  • 济宁的天气很灿烂 早上搭载顺风车 沿途的春光很美 路过太白湖的时候觉得有时间的话到此一游还是极好的 …… 不过比欣...
    羽衣甘蓝2025阅读 150评论 0 0