最近看到一篇文章介紹Vector兼容方案的吱七。參考http://www.reibang.com/p/e3614e7abc03
嘗試寫(xiě)了下Demo感覺(jué)還不錯(cuò),但是遇到一個(gè)問(wèn)題鹤竭,需要Vector的顏色需要?jiǎng)討B(tài)調(diào)整踊餐,前面的文章里通過(guò)屬性動(dòng)畫(huà)一步步是可以實(shí)現(xiàn)這個(gè)效果的,但我只是改個(gè)顏色又是要寫(xiě)xml又是要用動(dòng)畫(huà)臀稚,這有點(diǎn)大炮打蚊子了吝岭,而且動(dòng)畫(huà)持續(xù)時(shí)間設(shè)置多少算合適?屬性動(dòng)畫(huà)肯定不是優(yōu)雅的解決方案吧寺。屬性動(dòng)畫(huà)無(wú)非就是反射窜管,既然屬性動(dòng)畫(huà)能修改的,那我們自己通過(guò)反射肯定也是可以修改的稚机。
這篇文章就是介紹下如何避免使用屬性動(dòng)畫(huà)幕帆,來(lái)修改VectorDrawable的屬性。其實(shí)整個(gè)過(guò)程就是查找需要反射的屬性路徑的過(guò)程赖条,只要知道什么是反射失乾,基本沒(méi)什么難度。
既然是反射纬乍,那怎么反射呢碱茁?既然有源碼,跟進(jìn)源碼看看AnimatedVectorDrawable具體是如何操作的仿贬。
<code>
Drawable drawable = imageView.getDrawable();
if (drawable instanceof Animatable) {
((Animatable) drawable).start();
}
</code>
上面是前面文章給的開(kāi)啟Vector動(dòng)畫(huà)的代碼纽竣。我們跟進(jìn)start看看。很不幸是個(gè)接口茧泪。這時(shí)候怎么辦蜓氨?直接debug watch drawable的真實(shí)類,或者打印日志drawable.getClass().toString()看下真實(shí)的類队伟。
這里debug看到drawable其實(shí)是一個(gè)AnimatedVectorDrawable(注意這里Api21以前是兼容包的類穴吹,這里因?yàn)槲业氖謾C(jī)是6.0所以這里是AnimatedVectorDrawable,其實(shí)原理完全一樣)缰泡;
跟進(jìn)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>
東西全出來(lái)了刀荒。看見(jiàn)mAnimatorSet.start();寫(xiě)過(guò)屬性動(dòng)畫(huà)棘钞,應(yīng)該會(huì)非常熟悉缠借。跟進(jìn)ensureAnimatorSet方法
<code>
private void ensureAnimatorSet() {
if (!mHasAnimatorSet) {
mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes);
mHasAnimatorSet = true;
mRes = null;
}
}
</code>
沒(méi)什么好想的,繼續(xù)跟進(jìn)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>
這時(shí)候如果眼睛亮的話泼返,應(yīng)該會(huì)直接看見(jiàn)這一行
<code>
final Animator firstAnim = prepareLocalAnimator(0);
</code>
prepareLocalAnimator這里返回了一個(gè)Animator,跟進(jìn)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>
一下子就豁然開(kāi)朗了绅喉,
<code>
final Object target = mVectorDrawable.getTargetByName(targetName);
</code>
這一行IDE報(bào)了紅線渠鸽,表示這個(gè)方法不可訪問(wèn)。VectorDrawable里其實(shí)有這個(gè)方法
<code>
Object getTargetByName(String name) {
return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
}
</code>
好多層柴罐。我們跟到mVGTargetsMap那里看看那里有什么徽缚。可惜的是這個(gè)Map保存是Object類型革屠,這時(shí)候我們可以繼續(xù)debug watch這個(gè)Map查看里面的Object的運(yùn)行時(shí)的實(shí)際類型凿试,不過(guò)向下掃一眼看到了mVGTargetsMap所在的類叫VPathRenderer,這個(gè)類的構(gòu)造方法是這個(gè):
<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>
倒數(shù)第三行put了this似芝,這表示返回的target其實(shí)就是當(dāng)前類那婉。當(dāng)前類保存在Map里浓恳,然后Map會(huì)通過(guò)
<code>
Object getTargetByName(String name) {
return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
}
</code>
返回作為Animator的tartget并闲,這時(shí)候想到了什么?屬性動(dòng)畫(huà)原理就是反射getset晰绎,那這個(gè)類非衬椋可能就有mFillColor屬性或者setFillColor方法呛谜。command+F12輸入setfil
好吧,終于發(fā)現(xiàn)你了蝇闭。
<code>
@SuppressWarnings("unused")
void setFillColor(int fillColor) {
mFillColor = fillColor;
}
</code>
其實(shí)不光是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>
到這里剩下的應(yīng)該就不用說(shuō)了,思路基本已經(jīng)有了呻引,剩下的就簡(jiǎn)單了,無(wú)非就是敲鍵盤(pán)了吐咳。如果你不想敲也可以參考這里:
https://github.com/aesean/VectorHelper/blob/master/VectorHelper
保留作者逻悠,保留原始鏈接后,歡迎轉(zhuǎn)載韭脊。原始鏈接:http://www.reibang.com/p/2db6a5ce871b