文章轉(zhuǎn)載至郭神的博客
大家好涛癌,歡迎繼續(xù)回到Android屬性動(dòng)畫完全解析。在上一篇文章當(dāng)中我們學(xué)習(xí)了屬性動(dòng)畫的一些進(jìn)階技巧窥浪,包括ValueAnimator和ObjectAnimator的高級(jí)用法祖很,那么除了這些之外,當(dāng)然還有一些其它的高級(jí)技巧在等著我們學(xué)習(xí)漾脂,因此本篇文章就對(duì)整個(gè)屬性動(dòng)畫完全解析系列收個(gè)尾假颇,來(lái)學(xué)習(xí)一下剩下的非常重要的高級(jí)技巧。
另外骨稿,本篇文章中使用的代碼是建立在上篇文章基礎(chǔ)之上的笨鸡,如果你還沒有閱讀過(guò)前面的文章,建議先去參考閱讀一下 Android屬性動(dòng)畫完全解析(中)坦冠,ValueAnimator和ObjectAnimator的高級(jí)用法 形耗。
Interpolator的用法
Interpolator這個(gè)東西很難進(jìn)行翻譯,直譯過(guò)來(lái)的話是補(bǔ)間器的意思辙浑,它的主要作用是可以控制動(dòng)畫的變化速率激涤,比如去實(shí)現(xiàn)一種非線性運(yùn)動(dòng)的動(dòng)畫效果。那么什么叫做非線性運(yùn)動(dòng)的動(dòng)畫效果呢判呕?就是說(shuō)動(dòng)畫改變的速率不是一成不變的倦踢,像加速運(yùn)動(dòng)以及減速運(yùn)動(dòng)都屬于非線性運(yùn)動(dòng)。
不過(guò)Interpolator并不是屬性動(dòng)畫中新增的技術(shù)侠草,實(shí)際上從Android 1.0版本開始就一直存在Interpolator接口了辱挥,而之前的補(bǔ)間動(dòng)畫當(dāng)然也是支持這個(gè)功能的。只不過(guò)在屬性動(dòng)畫中新增了一個(gè)TimeInterpolator接口边涕,這個(gè)接口是用于兼容之前的Interpolator的晤碘,這使得所有過(guò)去的Interpolator實(shí)現(xiàn)類都可以直接拿過(guò)來(lái)放到屬性動(dòng)畫當(dāng)中使用褂微,那么我們來(lái)看一下現(xiàn)在TimeInterpolator接口的所有實(shí)現(xiàn)類,如下圖所示:
可以看到园爷,TimeInterpolator接口已經(jīng)有非常多的實(shí)現(xiàn)類了宠蚂,這些都是Android系統(tǒng)內(nèi)置好的并且我們可以直接使用的Interpolator。每個(gè)Interpolator都有它各自的實(shí)現(xiàn)效果腮介,比如說(shuō)AccelerateInterpolator就是一個(gè)加速運(yùn)動(dòng)的Interpolator肥矢,而DecelerateInterpolator就是一個(gè)減速運(yùn)動(dòng)的Interpolator。
我覺得細(xì)心的朋友應(yīng)該早已經(jīng)發(fā)現(xiàn)了叠洗,在前面兩篇文章當(dāng)中我們所學(xué)到的所有屬性動(dòng)畫甘改,其實(shí)都不是在進(jìn)行一種線程運(yùn)動(dòng)。比如說(shuō)在“上”篇文章中使用ValueAnimator所打印的值如下所示:
可以看到灭抑,一開始的值變化速度明顯比較慢十艾,僅0.0開頭的就打印了4次,之后開始加速腾节,最后階段又開始減速忘嫉,因此我們可以很明顯地看出這一個(gè)先加速后減速的Interpolator。
那么再來(lái)看一下在“中”篇文章中完成的小球移動(dòng)加變色的功能案腺,如下圖所示:
從上圖中我們明顯可以看出庆冕,小球一開始運(yùn)動(dòng)速度比較慢,然后逐漸加速劈榨,中間的部分運(yùn)動(dòng)速度就比較快访递,接下來(lái)開始減速,最后緩緩?fù)W⊥薄A硗忸伾兓彩沁@種規(guī)律拷姿,一開始顏色變化的比較慢,中間顏色變化的很快旱函,最后階段顏色變化的又比較慢响巢。
從以上幾點(diǎn)我們就可以總結(jié)出一個(gè)結(jié)論了,使用屬性動(dòng)畫時(shí)棒妨,系統(tǒng)默認(rèn)的Interpolator其實(shí)就是一個(gè)先加速后減速的Interpolator踪古,對(duì)應(yīng)的實(shí)現(xiàn)類就是AccelerateDecelerateInterpolator。
當(dāng)然券腔,我們也可以很輕松地修改這一默認(rèn)屬性伏穆,將它替換成任意一個(gè)系統(tǒng)內(nèi)置好的Interpolator。就拿“中”篇文章中的代碼來(lái)舉例吧颅眶,MyAnimView中的startAnimation()方法是開啟動(dòng)畫效果的入口,這里我們對(duì)Point對(duì)象的坐標(biāo)稍做一下修改田弥,讓它變成一種垂直掉落的效果涛酗,代碼如下所示:
private void startAnimation() {
Point startPoint = new Point(getWidth() / 2, RADIUS);
Point endPoint = new Point(getWidth() / 2, getHeight() - RADIUS);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
anim.setDuration(2000);
anim.start();
}
這里主要是對(duì)Point構(gòu)造函數(shù)中的坐標(biāo)值進(jìn)行了一下改動(dòng),那么現(xiàn)在小球運(yùn)動(dòng)的動(dòng)畫效果應(yīng)該是從屏幕正中央的頂部掉落到底部。但是現(xiàn)在默認(rèn)情況下小球的下降速度肯定是先加速后減速的商叹,這不符合物理的常識(shí)規(guī)律燕刻,如果把小球視為一個(gè)自由落體的話,那么下降的速度應(yīng)該是越來(lái)越快的剖笙。我們?cè)鯓硬拍芨淖冞@一默認(rèn)行為呢卵洗?其實(shí)很簡(jiǎn)單,調(diào)用Animator的setInterpolator()方法就可以了弥咪,這個(gè)方法要求傳入一個(gè)實(shí)現(xiàn)TimeInterpolator接口的實(shí)例过蹂,那么比如說(shuō)我們想要實(shí)現(xiàn)小球下降越來(lái)越快的效果,就可以使用AccelerateInterpolator聚至,代碼如下所示:
private void startAnimation() {
Point startPoint = new Point(getWidth() / 2, RADIUS);
Point endPoint = new Point(getWidth() / 2, getHeight() - RADIUS);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
anim.setInterpolator(new AccelerateInterpolator(2f));
anim.setDuration(2500);
anim.start();
}
代碼很簡(jiǎn)單酷勺,這里調(diào)用了setInterpolator()方法,然后傳入了一個(gè)AccelerateInterpolator的實(shí)例扳躬,注意AccelerateInterpolator的構(gòu)建函數(shù)可以接收一個(gè)float類型的參數(shù)脆诉,這個(gè)參數(shù)是用于控制加速度的。現(xiàn)在運(yùn)行一下代碼贷币,效果如下圖所示:
OK击胜,效果非常明顯,說(shuō)明我們已經(jīng)成功替換掉了默認(rèn)的Interpolator役纹,AccelerateInterpolator確實(shí)是生效了偶摔。但是現(xiàn)在的動(dòng)畫效果看上去仍然是怪怪的妄呕,因?yàn)橐粋€(gè)小球從很高的地方掉落到地面上直接就靜止了崎页,這也是不符合物理規(guī)律的陡蝇,小球撞擊到地面之后應(yīng)該要反彈起來(lái)膛腐,然后再次落下陷猫,接著再反彈起來(lái)奠支,又再次落下抬探,以此反復(fù)组橄,最后靜止硫戈。這個(gè)功能我們當(dāng)然可以自己去寫锰什,只不過(guò)比較復(fù)雜,所幸的是丁逝,Android系統(tǒng)中已經(jīng)提供好了這樣一種Interpolator汁胆,我們只需要簡(jiǎn)單地替換一下就可以完成上面的描述的效果,代碼如下所示:
private void startAnimation() {
Point startPoint = new Point(getWidth() / 2, RADIUS);
Point endPoint = new Point(getWidth() / 2, getHeight() - RADIUS);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
anim.setInterpolator(new BounceInterpolator());
anim.setDuration(3000);
anim.start();
}
可以看到霜幼,我們只是將設(shè)置的Interpolator換成了BounceInterpolator的實(shí)例嫩码,而BounceInterpolator就是一種可以模擬物理規(guī)律,實(shí)現(xiàn)反復(fù)彈起效果的Interpolator罪既。另外還將整體的動(dòng)畫時(shí)間稍微延長(zhǎng)了一點(diǎn)铸题,因?yàn)樾∏蚍磸?fù)彈起需要比之前更長(zhǎng)的時(shí)間≌∷。現(xiàn)在重新運(yùn)行一下代碼,效果如下圖所示:
OK丢间!效果還是非常不錯(cuò)的探熔。那么這里我們只是選了幾個(gè)系統(tǒng)實(shí)現(xiàn)好的Interpolator,由于內(nèi)置Interpolator非常多烘挫,就不一一進(jìn)行講解了诀艰,大家可以自己去使用一下其它的幾種Interpolator來(lái)看一看效果。
但是饮六,只會(huì)用一下系統(tǒng)提供好的Interpolator其垄,我們顯然對(duì)自己的要求就太低了,既然是學(xué)習(xí)屬性動(dòng)畫的高級(jí)用法喜滨,那么自然要將它研究透了捉捅。下面我們就來(lái)看一下Interpolator的內(nèi)部實(shí)現(xiàn)機(jī)制是什么樣的,并且來(lái)嘗試寫一個(gè)自定義的Interpolator虽风。
首先看一下TimeInterpolator的接口定義棒口,代碼如下所示:
/**
* A time interpolator defines the rate of change of an animation. This allows animations
* to have non-linear motion, such as acceleration and deceleration.
*/
public interface TimeInterpolator {
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* @return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
float getInterpolation(float input);
}
OK,接口還是非常簡(jiǎn)單的辜膝,只有一個(gè)getInterpolation()方法无牵。大家有興趣可以通過(guò)注釋來(lái)對(duì)這個(gè)接口進(jìn)行詳解的了解,這里我就簡(jiǎn)單解釋一下厂抖,getInterpolation()方法中接收一個(gè)input參數(shù)茎毁,這個(gè)參數(shù)的值會(huì)隨著動(dòng)畫的運(yùn)行而不斷變化,不過(guò)它的變化是非常有規(guī)律的忱辅,就是根據(jù)設(shè)定的動(dòng)畫時(shí)長(zhǎng)勻速增加七蜘,變化范圍是0到1。也就是說(shuō)當(dāng)動(dòng)畫一開始的時(shí)候input的值是0墙懂,到動(dòng)畫結(jié)束的時(shí)候input的值是1橡卤,而中間的值則是隨著動(dòng)畫運(yùn)行的時(shí)長(zhǎng)在0到1之間變化的。
說(shuō)到這個(gè)input的值损搬,我覺得有不少朋友可能會(huì)聯(lián)想到我們?cè)凇爸小逼恼轮惺褂眠^(guò)的fraction值碧库。那么這里的input和fraction有什么關(guān)系或者區(qū)別呢?答案很簡(jiǎn)單巧勤,input的值決定了fraction的值嵌灰。input的值是由系統(tǒng)經(jīng)過(guò)計(jì)算后傳入到getInterpolation()方法中的,然后我們可以自己實(shí)現(xiàn)getInterpolation()方法中的算法颅悉,根據(jù)input的值來(lái)計(jì)算出一個(gè)返回值沽瞭,而這個(gè)返回值就是fraction了。
因此剩瓶,最簡(jiǎn)單的情況就是input值和fraction值是相同的驹溃,這種情況由于input值是勻速增加的柒瓣,因而fraction的值也是勻速增加的,所以動(dòng)畫的運(yùn)動(dòng)情況也是勻速的吠架。系統(tǒng)中內(nèi)置的LinearInterpolator就是一種勻速運(yùn)動(dòng)的Interpolator,那么我們來(lái)看一下它的源碼是怎么實(shí)現(xiàn)的:
/**
* An interpolator where the rate of change is constant
*/
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createLinearInterpolator();
}
}
這里我們只看getInterpolation()方法搂鲫,這個(gè)方法沒有任何邏輯傍药,就是把參數(shù)中傳遞的input值直接返回了,因此fraction的值就是等于input的值的魂仍,這就是勻速運(yùn)動(dòng)的Interpolator的實(shí)現(xiàn)方式拐辽。
當(dāng)然這是最簡(jiǎn)單的一種Interpolator的實(shí)現(xiàn)了,我們?cè)賮?lái)看一個(gè)稍微復(fù)雜一點(diǎn)的擦酌。既然現(xiàn)在大家都知道了系統(tǒng)在默認(rèn)情況下使用的是AccelerateDecelerateInterpolator俱诸,那我們就來(lái)看一下它的源碼吧,如下所示:
/**
* An interpolator where the rate of change starts and ends slowly but
* accelerates through the middle.
*
*/
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
public AccelerateDecelerateInterpolator() {
}
@SuppressWarnings({"UnusedDeclaration"})
public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
}
}
代碼雖然沒有變長(zhǎng)很多赊舶,但是getInterpolation()方法中的邏輯已經(jīng)明顯變復(fù)雜了睁搭,不再是簡(jiǎn)單地將參數(shù)中的input進(jìn)行返回,而是進(jìn)行了一個(gè)較為復(fù)雜的數(shù)學(xué)運(yùn)算笼平。那這里我們來(lái)分析一下它的算法實(shí)現(xiàn)园骆,可以看到,算法中主要使用了余弦函數(shù)寓调,由于input的取值范圍是0到1锌唾,那么cos函數(shù)中的取值范圍就是π到2π。而cos(π)的結(jié)果是-1夺英,cos(2π)的結(jié)果是1晌涕,那么這個(gè)值再除以2加上0.5之后,getInterpolation()方法最終返回的結(jié)果值還是在0到1之間痛悯。只不過(guò)經(jīng)過(guò)了余弦運(yùn)算之后余黎,最終的結(jié)果不再是勻速增加的了,而是經(jīng)歷了一個(gè)先加速后減速的過(guò)程灸蟆。我們可以將這個(gè)算法的執(zhí)行情況通過(guò)曲線圖的方式繪制出來(lái)驯耻,結(jié)果如下圖所示:
可以看到,這是一個(gè)S型的曲線圖炒考,當(dāng)橫坐標(biāo)從0變化到0.2的時(shí)候可缚,縱坐標(biāo)的變化幅度很小,但是之后就開始明顯加速斋枢,最后橫坐標(biāo)從0.8變化到1的時(shí)候帘靡,縱坐標(biāo)的變化幅度又變得很小。
OK瓤帚,通過(guò)分析LinearInterpolator和AccelerateDecelerateInterpolator的源碼描姚,我們已經(jīng)對(duì)Interpolator的內(nèi)部實(shí)現(xiàn)機(jī)制有了比較清楚的認(rèn)識(shí)了涩赢,那么接下來(lái)我們就開始嘗試編寫一個(gè)自定義的Interpolator。
編寫自定義Interpolator最主要的難度都是在于數(shù)學(xué)計(jì)算方面的轩勘,由于我數(shù)學(xué)并不是很好筒扒,因此這里也就寫一個(gè)簡(jiǎn)單點(diǎn)的Interpolator來(lái)給大家演示一下。既然屬性動(dòng)畫默認(rèn)的Interpolator是先加速后減速的一種方式绊寻,這里我們就對(duì)它進(jìn)行一個(gè)簡(jiǎn)單的修改花墩,讓它變成先減速后加速的方式。新建DecelerateAccelerateInterpolator類澄步,讓它實(shí)現(xiàn)TimeInterpolator接口冰蘑,代碼如下所示:
public class DecelerateAccelerateInterpolator implements TimeInterpolator{
@Override
public float getInterpolation(float input) {
float result;
if (input <= 0.5) {
result = (float) (Math.sin(Math.PI * input)) / 2;
} else {
result = (float) (2 - Math.sin(Math.PI * input)) / 2;
}
return result;
}
}
這段代碼是使用正弦函數(shù)來(lái)實(shí)現(xiàn)先減速后加速的功能的,因?yàn)檎液瘮?shù)初始弧度的變化值非常大村缸,剛好和余弦函數(shù)是相反的祠肥,而隨著弧度的增加,正弦函數(shù)的變化值也會(huì)逐漸變小梯皿,這樣也就實(shí)現(xiàn)了減速的效果仇箱。當(dāng)弧度大于π/2之后,整個(gè)過(guò)程相反了過(guò)來(lái)东羹,現(xiàn)在正弦函數(shù)的弧度變化值非常小工碾,漸漸隨著弧度繼續(xù)增加,變化值越來(lái)越大百姓,弧度到π時(shí)結(jié)束渊额,這樣從0過(guò)度到π,也就實(shí)現(xiàn)了先減速后加速的效果垒拢。
同樣我們可以將這個(gè)算法的執(zhí)行情況通過(guò)曲線圖的方式繪制出來(lái)旬迹,結(jié)果如下圖所示:
可以看到,這也是一個(gè)S型的曲線圖求类,只不過(guò)曲線的方向和剛才是相反的奔垦。從上圖中我們可以很清楚地看出來(lái),一開始縱坐標(biāo)的變化幅度很大尸疆,然后逐漸變小椿猎,橫坐標(biāo)到0.5的時(shí)候縱坐標(biāo)變化幅度趨近于零,之后隨著橫坐標(biāo)繼續(xù)增加縱坐標(biāo)的變化幅度又開始變大寿弱,的確是先減速后加速的效果犯眠。
那么現(xiàn)在我們將DecelerateAccelerateInterpolator在代碼中進(jìn)行替換,如下所示:
private void startAnimation() {
Point startPoint = new Point(getWidth() / 2, RADIUS);
Point endPoint = new Point(getWidth() / 2, getHeight() - RADIUS);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
anim.setInterpolator(new DecelerateAccelerateInterpolator());
anim.setDuration(3000);
anim.start();
}
非常簡(jiǎn)單症革,就是將DecelerateAccelerateInterpolator的實(shí)例傳入到setInterpolator()方法當(dāng)中筐咧。重新運(yùn)行一下代碼,效果如下圖所示:
OK!小球的運(yùn)動(dòng)確實(shí)是先減速后加速的效果量蕊,說(shuō)明我們自定義的Interpolator已經(jīng)可以正常工作了铺罢。通過(guò)這樣一個(gè)程度的學(xué)習(xí),相信大家對(duì)屬性動(dòng)畫Interpolator的理解和使用都達(dá)到了一個(gè)比較深刻的層次了残炮。
ViewPropertyAnimator的用法
ViewPropertyAnimator其實(shí)算不上什么高級(jí)技巧韭赘,它的用法格外的簡(jiǎn)單,只不過(guò)和前面所學(xué)的所有屬性動(dòng)畫的知識(shí)不同势就,它并不是在3.0系統(tǒng)當(dāng)中引入的辞居,而是在3.1系統(tǒng)當(dāng)中附增的一個(gè)新的功能,因此這里我們把它作為整個(gè)屬性動(dòng)畫系列的收尾部分蛋勺。
我們都知道,屬性動(dòng)畫的機(jī)制已經(jīng)不是再針對(duì)于View而進(jìn)行設(shè)計(jì)的了鸠删,而是一種不斷地對(duì)值進(jìn)行操作的機(jī)制抱完,它可以將值賦值到指定對(duì)象的指定屬性上。但是刃泡,在絕大多數(shù)情況下巧娱,我相信大家主要都還是對(duì)View進(jìn)行動(dòng)畫操作的。Android開發(fā)團(tuán)隊(duì)也是意識(shí)到了這一點(diǎn)烘贴,沒有為View的動(dòng)畫操作提供一種更加便捷的用法確實(shí)是有點(diǎn)太不人性化了禁添,于是在Android 3.1系統(tǒng)當(dāng)中補(bǔ)充了ViewPropertyAnimator這個(gè)機(jī)制。
那我們先來(lái)回顧一下之前的用法吧桨踪,比如我們想要讓一個(gè)TextView從常規(guī)狀態(tài)變成透明狀態(tài)老翘,就可以這樣寫:
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 0f);
animator.start();
看上去復(fù)雜嗎?好像也不怎么復(fù)雜锻离,但確實(shí)也不怎么容易理解铺峭。我們要將操作的view、屬性汽纠、變化的值都一起傳入到ObjectAnimator.ofFloat()方法當(dāng)中卫键,雖然看上去也沒寫幾行代碼,但這不太像是我們平時(shí)使用的面向?qū)ο蟮乃季S虱朵。
那么下面我們就來(lái)看一下如何使用ViewPropertyAnimator來(lái)實(shí)現(xiàn)同樣的效果莉炉,ViewPropertyAnimator提供了更加易懂、更加面向?qū)ο蟮腁PI碴犬,如下所示:
textview.animate().alpha(0f);
果然非常簡(jiǎn)單絮宁!不過(guò)textview.animate()這個(gè)方法是怎么回事呢?animate()方法就是在Android 3.1系統(tǒng)上新增的一個(gè)方法服协,這個(gè)方法的返回值是一個(gè)ViewPropertyAnimator對(duì)象羞福,也就是說(shuō)拿到這個(gè)對(duì)象之后我們就可以調(diào)用它的各種方法來(lái)實(shí)現(xiàn)動(dòng)畫效果了,這里我們調(diào)用了alpha()方法并轉(zhuǎn)入0蚯涮,表示將當(dāng)前的textview變成透明狀態(tài)治专。
怎么樣卖陵?比起使用ObjectAnimator,ViewPropertyAnimator的用法明顯更加簡(jiǎn)單易懂吧张峰。除此之外泪蔫,ViewPropertyAnimator還可以很輕松地將多個(gè)動(dòng)畫組合到一起,比如我們想要讓textview運(yùn)動(dòng)到500,500這個(gè)坐標(biāo)點(diǎn)上喘批,就可以這樣寫:
textview.animate().x(500).y(500);
可以看出撩荣,ViewPropertyAnimator是支持連綴用法的,我們想讓textview移動(dòng)到橫坐標(biāo)500這個(gè)位置上時(shí)調(diào)用了x(500)這個(gè)方法饶深,然后讓textview移動(dòng)到縱坐標(biāo)500這個(gè)位置上時(shí)調(diào)用了y(500)這個(gè)方法餐曹,將所有想要組合的動(dòng)畫通過(guò)這種連綴的方式拼接起來(lái),這樣全部動(dòng)畫就都會(huì)一起被執(zhí)行敌厘。
那么怎樣去設(shè)定動(dòng)畫的運(yùn)行時(shí)長(zhǎng)呢台猴?很簡(jiǎn)單,也是通過(guò)連綴的方式設(shè)定即可俱两,比如我們想要讓動(dòng)畫運(yùn)行5秒鐘饱狂,就可以這樣寫:
textview.animate().x(500).y(500).setDuration(5000);
除此之外,本篇文章第一部分所學(xué)的Interpolator技術(shù)我們也可以應(yīng)用在ViewPropertyAnimator上面宪彩,如下所示:
textview.animate().x(500).y(500).setDuration(5000)
.setInterpolator(new BounceInterpolator());
用法很簡(jiǎn)單休讳,同樣也是使用連綴的方式。相信大家現(xiàn)在都已經(jīng)體驗(yàn)出來(lái)了尿孔,ViewPropertyAnimator其實(shí)并沒有什么太多的技巧可言俊柔,用法基本都是大同小異的,需要用到什么功能就連綴一下活合,因此更多的用法大家只需要去查閱一下文檔婆咸,看看還支持哪些功能,有哪些接口可以調(diào)用就可以了芜辕。
那么除了用法之外尚骄,關(guān)于ViewPropertyAnimator有幾個(gè)細(xì)節(jié)還是值得大家注意一下的:
- 整個(gè)ViewPropertyAnimator的功能都是建立在View類新增的animate()方法之上的,這個(gè)方法會(huì)創(chuàng)建并返回一個(gè)ViewPropertyAnimator的實(shí)例侵续,之后的調(diào)用的所有方法倔丈,設(shè)置的所有屬性都是通過(guò)這個(gè)實(shí)例完成的。
- 大家注意到状蜗,在使用ViewPropertyAnimator時(shí)需五,我們自始至終沒有調(diào)用過(guò)start()方法,這是因?yàn)樾碌慕涌谥惺褂昧穗[式啟動(dòng)動(dòng)畫的功能轧坎,只要我們將動(dòng)畫定義完成之后宏邮,動(dòng)畫就會(huì)自動(dòng)啟動(dòng)。并且這個(gè)機(jī)制對(duì)于組合動(dòng)畫也同樣有效,只要我們不斷地連綴新的方法蜜氨,那么動(dòng)畫就不會(huì)立刻執(zhí)行械筛,等到所有在ViewPropertyAnimator上設(shè)置的方法都執(zhí)行完畢后,動(dòng)畫就會(huì)自動(dòng)啟動(dòng)飒炎。當(dāng)然如果不想使用這一默認(rèn)機(jī)制的話埋哟,我們也可以顯式地調(diào)用start()方法來(lái)啟動(dòng)動(dòng)畫。
- ViewPropertyAnimator的所有接口都是使用連綴的語(yǔ)法來(lái)設(shè)計(jì)的郎汪,每個(gè)方法的返回值都是它自身的實(shí)例赤赊,因此調(diào)用完一個(gè)方法之后可以直接連綴調(diào)用它的另一個(gè)方法,這樣把所有的功能都串接起來(lái)煞赢,我們甚至可以僅通過(guò)一行代碼就完成任意復(fù)雜度的動(dòng)畫功能抛计。