Android動(dòng)畫(huà)教程之屬性動(dòng)畫(huà)詳解
簡(jiǎn)介
Android 開(kāi)發(fā)中,總是需要一些動(dòng)畫(huà)來(lái)優(yōu)化用戶的交互體驗(yàn),提高用戶滿意度。因此,Google 為我們提供了一些用于處理動(dòng)畫(huà)效果的動(dòng)畫(huà)框架。Android 的動(dòng)畫(huà)框架分為兩類:
- 傳統(tǒng)動(dòng)畫(huà)(Animation):通過(guò)系統(tǒng)不斷調(diào)用onDraw方法重繪界面,來(lái)達(dá)到動(dòng)畫(huà)的效果。
- 屬性動(dòng)畫(huà)(Animator):通過(guò)操縱一個(gè)屬性的get/set方法,真實(shí)地改變目標(biāo)的某些屬性。
傳統(tǒng)動(dòng)畫(huà)框架的局限性
既然有了傳統(tǒng)動(dòng)畫(huà)框架,Google 為什么還要?jiǎng)?chuàng)造一個(gè)屬性動(dòng)畫(huà)框架呢?
我們下面舉個(gè)例子來(lái)說(shuō)明一下傳統(tǒng)動(dòng)畫(huà)的局限性。
在布局中加入一個(gè) ImageView 和一個(gè) Button,點(diǎn)擊 ImageView 后彈出一個(gè) Toast,點(diǎn)擊 Button 后使 ImageView 展現(xiàn)一個(gè)向右平移的動(dòng)畫(huà)效果。
下面是使用傳統(tǒng)動(dòng)畫(huà)實(shí)現(xiàn)的代碼:
TranslateAnimation animation = new TranslateAnimation(0,200,0,0); // 平移動(dòng)畫(huà)x軸移動(dòng)200,y軸不動(dòng) animation.setDuration(1000); // 動(dòng)畫(huà)時(shí)長(zhǎng) animation.setFillAfter(true); // 使動(dòng)畫(huà)結(jié)束后停留在結(jié)束的位置 mIvPicture.startAnimation(animation);
運(yùn)行后,ImageView 確實(shí)進(jìn)行了我們預(yù)期的平移的效果。可是當(dāng)我們嘗試點(diǎn)擊 ImageView 當(dāng)前的位置時(shí),卻沒(méi)有 Toast 彈出。我們?cè)賴L試去點(diǎn)擊 ImageView 開(kāi)始動(dòng)畫(huà)前的位置,卻成功彈出了 Toast。
這就是傳統(tǒng)動(dòng)畫(huà)很大的局限性:
- 它僅僅是重繪了控件,改變了其顯示的位置。但真正事件響應(yīng)的位置,卻并沒(méi)有發(fā)生改變。因此傳統(tǒng)動(dòng)畫(huà)不適合做具有交互的動(dòng)畫(huà)效果。僅僅能做一些顯示的動(dòng)畫(huà)效果。
- 傳統(tǒng)動(dòng)畫(huà)是不斷通過(guò) onDraw() 方法重繪界面,必然會(huì)十分耗費(fèi)GPU資源。
- 傳統(tǒng)動(dòng)畫(huà)所支持的動(dòng)畫(huà)類型少,僅有旋轉(zhuǎn)、縮放、位移、透明度這四種動(dòng)畫(huà)效果。雖然通過(guò)組合可以實(shí)現(xiàn)豐富的效果,但相比直接通過(guò)改變屬性來(lái)實(shí)現(xiàn)的屬性動(dòng)畫(huà)來(lái)說(shuō),還是有很大的局限性的。
因此,Google 為我們提供了一套全新的屬性動(dòng)畫(huà)框架,來(lái)讓我們實(shí)現(xiàn)更豐富的動(dòng)畫(huà)效果。
ObjectAnimator
ObjectAnimator 是屬性動(dòng)畫(huà)中,最簡(jiǎn)單也最常用的一個(gè)對(duì)象。
實(shí)現(xiàn) Animation 框架的功能
平移
前文提到的使 ImageVIew 向右平移 200 像素的動(dòng)畫(huà)效果,使用屬性動(dòng)畫(huà)只需要很簡(jiǎn)單的幾句代碼即可實(shí)現(xiàn):
ObjectAnimator.ofFloat(mIvPicture,"translationX",0F,200F) .setDuration(1000) .start();
我們來(lái)分析一下這一句代碼。我們調(diào)用了ofFloat代碼,并傳入三個(gè)參數(shù)。
第一個(gè)參數(shù)是動(dòng)畫(huà)需要操縱的目標(biāo),在這里是我們的 ImageView。
第二個(gè)參數(shù)是所需要操縱的目標(biāo)所具備的屬性名稱。
第三個(gè)參數(shù)是這個(gè)動(dòng)畫(huà)變化的取值范圍。
最后設(shè)置一下它的動(dòng)畫(huà)的屬性,便可以 start 了。
這次我們?cè)俅吸c(diǎn)擊 ImageView 目前的位置,成功地彈出了 Toast。這證實(shí)了屬性動(dòng)畫(huà)是通過(guò)改變物體的屬性來(lái)達(dá)到動(dòng)畫(huà)效果的理論。
當(dāng)我們需要改變 y 坐標(biāo)時(shí),只需要把 "translationX" 變?yōu)?"translationY" 即可。
其實(shí) ,只要Google對(duì)一個(gè)對(duì)象的某個(gè)屬性提供了get和set方法,我們就可以使用這個(gè)屬性來(lái)實(shí)現(xiàn)動(dòng)畫(huà)效果。
其實(shí)我們還能用 X Y 兩個(gè)屬性實(shí)現(xiàn)之前的動(dòng)畫(huà)效果,那么對(duì)象屬性中 X 的 Y 與 translationX translationY 有什么區(qū)別呢?
translationX translationY指的是物體的偏移量,而X Y則表示它最終到達(dá)的絕對(duì)位置。
旋轉(zhuǎn)
旋轉(zhuǎn)屬性使用的是 "rotation" 屬性,后面的變換范圍的單位是角度。
比如想讓 ImageView 旋轉(zhuǎn)90度,只需要
ObjectAnimator.ofFloat(mIvPicture,"rotation",0F,90F) .setDuration(1000) .start();
其他
其實(shí)屬性動(dòng)畫(huà)能操縱的屬性,只要具有 set、get 方法,都可以進(jìn)行操縱。如 scaleX、scaleY 等等...
插值器
Android 為我們內(nèi)置了插值器,使我們的動(dòng)畫(huà)更為自然。比如可以讓我們的平移動(dòng)畫(huà)像物體的重力加速度由快到慢的 Accelerate 等等
Android中內(nèi)置了七種插值器,分別是
- Accelerate
- Decelerate
- Accelerate/Decelerate
- Anticipate
- Overshoot
- Anticipate/Overshoot
- Bounce
要應(yīng)用插值器,可以調(diào)用 ObjectAnimator 的 setInterpolator 方法, new 出對(duì)應(yīng)的插值器作為參數(shù)(xxxInterpolator)。比如下面這段代碼:
animator.setInterpolator(new AccelerateInterpolator());
通過(guò)插值器,我們可以讓動(dòng)畫(huà)的效果更佳自然。
多種屬性動(dòng)畫(huà)同時(shí)作用
當(dāng)我們把幾種動(dòng)畫(huà)按順序?qū)懴聲r(shí),運(yùn)行程序,會(huì)發(fā)現(xiàn)效果是三種屬性動(dòng)畫(huà)的疊加。由此可以發(fā)現(xiàn),屬性動(dòng)畫(huà)在調(diào)用 start 方法后,實(shí)際上是一個(gè)異步的過(guò)程。因此我們就可以看到三個(gè)屬性動(dòng)畫(huà)同時(shí)作用的效果。通過(guò)這樣的方法,其實(shí)就可以實(shí)現(xiàn)多種屬性動(dòng)畫(huà)同時(shí)作用的效果:
ObjectAnimator.ofFloat(mIvPicture,"translationX",0F,200F).setDuration(1000).start(); ObjectAnimator.ofFloat(mIvPicture,"rotationX",0F,360F).setDuration(1000).start(); ObjectAnimator.ofFloat(mIvPicture,"translationY",0F,200F).setDuration(1000).start();
其實(shí) Google 為我們提供了更好的方法,來(lái)實(shí)現(xiàn)這樣的效果。
我們可以使用 PropertyValuesHolder 來(lái)實(shí)現(xiàn)。其構(gòu)造函數(shù)僅僅比 ObjectAnimator 少了一個(gè)作用對(duì)象參數(shù)。之后通過(guò)ObjectAnimator 的 ofPropertyValuesHolder 方法,傳入作用對(duì)象以及要同時(shí)作用的 PropertyValuesHolder 即可執(zhí)行??梢钥吹较旅娴拇a示例:
PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("translationX",0F,200F); PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("rotationX",0F,360F); PropertyValuesHolder p3 = PropertyValuesHolder.ofFloat("translationY",0F,200F); ObjectAnimator.ofPropertyValuesHolder(mIvPicture,p1,p2,p3).setDuration(1000).start();
運(yùn)行后可以發(fā)現(xiàn),與之前的效果是相同的。
那既然兩種方法效果一樣,這樣相比之前有什么好處么?
- 其實(shí) Google 在 PropertyValuesHolder 內(nèi)部進(jìn)行了一些優(yōu)化,使得我們使用多個(gè)屬性動(dòng)畫(huà)時(shí)更加有效率,節(jié)省系統(tǒng)資源。
AnimatorSet 屬性集合
playTogether 方法
我們其實(shí)還可以通過(guò) AnimatorSet,來(lái)實(shí)現(xiàn)同樣的效果。這里我們調(diào)用了 set 的 playTogether 方法,使得這些方法同時(shí)執(zhí)行:
AnimatorSet set = new AnimatorSet(); ObjectAnimator animator1 = ObjectAnimator.ofFloat(mIvPicture, "translationX", 0F, 200F); ObjectAnimator animator2 = ObjectAnimator.ofFloat(mIvPicture, "rotationX", 0F, 360F); ObjectAnimator animator3 = ObjectAnimator.ofFloat(mIvPicture, "translationY", 0F, 200F); set.playTogether(animator1,animator2,animator3); set.setDuration(1000); set.start();
playSequentially方法
除了 playTogether 方法外,AnimatorSet 還提供了 playSequentially 方法,它可以使得動(dòng)畫(huà)按順序執(zhí)行。具體順序取決于調(diào)用時(shí)的參數(shù)順序。
AnimatorSet set = new AnimatorSet(); ObjectAnimator animator1 = ObjectAnimator.ofFloat(mIvPicture, "translationX", 0F, 200F); ObjectAnimator animator2 = ObjectAnimator.ofFloat(mIvPicture, "rotationX", 0F, 360F); ObjectAnimator animator3 = ObjectAnimator.ofFloat(mIvPicture, "translationY", 0F, 200F); set.playSequentially(animator1,animator2,animator3); set.setDuration(1000); set.start();
play 與 with、after 方法
我們除了可以用上述方法來(lái)讓動(dòng)畫(huà)按順序執(zhí)行外,也可以通過(guò) AnimatorSet 的 play、with、after、before 等方法相組合來(lái)控制動(dòng)畫(huà)播放關(guān)系。
例如如下的代碼就可以實(shí)現(xiàn)先平移,再旋轉(zhuǎn)的效果
set.play(animator1).with(animator3); set.play(animator2).after(animator1);
動(dòng)畫(huà)監(jiān)聽(tīng)事件
通過(guò)下面的代碼,我們可以實(shí)現(xiàn)按鈕按下后漸隱的效果。
mBtnPress.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { ObjectAnimator animator = ObjectAnimator.ofFloat(mBtnPress,"alpha",1F,0F); animator.setDuration(1000); animator.start(); } });
但如果我們想要在動(dòng)畫(huà)播放完成后再執(zhí)行一些操作的話,又該如何實(shí)現(xiàn)呢?
- 我們可以使用 ObjectAnimator 的 addListener方法,傳入一個(gè)AnimatorListener,為動(dòng)畫(huà)設(shè)置監(jiān)聽(tīng)事件。
AnimatorListener
一個(gè)AnimatorListener,需要實(shí)現(xiàn)四個(gè)方法,分別是:
- onAnimationStart
- onAnimationEnd
- onAnimationCancel
- onAnimationRepeat
它們的回調(diào)時(shí)機(jī)我們根據(jù)字面意思便可以理解。大部分時(shí)候,我們需要實(shí)現(xiàn)的是onAnimationEnd方法。
animator.addListener(new AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { Toast.makeText(MainActivity.this,"Animation End",Toast.LENGTH_SHORT).show(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } });
AnimatorListenerAdapter
如果每次監(jiān)聽(tīng)都需要實(shí)現(xiàn)這么多方法,未免太麻煩了一點(diǎn)。因此 Android 為我們提供了另一種方法來(lái)添加動(dòng)畫(huà)的監(jiān)聽(tīng)事件:在添加 AnimatorListener 的時(shí)候,傳入 AnimatorListenerAdapter 即可。這樣我們就只需要實(shí)現(xiàn)自己需要的方法即可。
animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); Toast.makeText(MainActivity.this,"Animation End",Toast.LENGTH_SHORT).show(); } });
ValueAnimator
簡(jiǎn)介
ValueAnimator 本身不作用于任何一個(gè)屬性,也不提供任何一種動(dòng)畫(huà)。它就是一個(gè)數(shù)值發(fā)生器,可以產(chǎn)生想要的各種數(shù)值。Android 系統(tǒng)為它提供了很多計(jì)算數(shù)值的方法,如 int、float 等等。我們也可以自己實(shí)現(xiàn)計(jì)算數(shù)值的方法。其實(shí),在屬性動(dòng)畫(huà)中,如何產(chǎn)生每一步的動(dòng)畫(huà)效果,都是通過(guò) ValueAnimator 計(jì)算出來(lái)的。
比如我們要實(shí)現(xiàn)一個(gè)從 0-100 的位移動(dòng)畫(huà)。隨著動(dòng)畫(huà)時(shí)間的持續(xù),它產(chǎn)生的值也會(huì)從 0-100 遞增。通過(guò)這個(gè) ValueAnimator 產(chǎn)生的值,再進(jìn)行屬性的設(shè)置即可。
那么 ValueAnimator 究竟是如何產(chǎn)生這些值的呢?
- 首先 ValueAnimator會(huì)根據(jù)會(huì)根據(jù)動(dòng)畫(huà)已進(jìn)行的時(shí)間與它持續(xù)的總時(shí)間的比值,產(chǎn)生一個(gè)0-1的時(shí)間因子。有了這樣的時(shí)間因子,經(jīng)過(guò)相應(yīng)的變換,就可以根據(jù)初始值和最終值來(lái)生成中間的相應(yīng)值。同時(shí),通過(guò)插值器的使用,我們還可以進(jìn)一步控制每一個(gè)時(shí)間因子產(chǎn)生值的變化速率。如果我們使用的是線性插值器,那么它生成值的時(shí)候就會(huì)呈一個(gè)線性變化。如果我們使用一個(gè)加速度插值器,那么它生成值時(shí)便會(huì)呈一個(gè)二次曲線,增長(zhǎng)率越來(lái)越快。
由于 ValueAnimator 不作用于任何一個(gè)屬性,也不提供任何一種動(dòng)畫(huà)。因此并沒(méi)有 ObjectAnimator 使用得廣泛。
實(shí)際上,ObjectAnimator 就是基于 ValueAnimator 進(jìn)行的一次封裝。我們可以查看 ObjectAnimator 的源碼,會(huì)發(fā)現(xiàn)它繼承自 ValueAnimator,是它的一個(gè)子類。正是 ValueAnimator 產(chǎn)生的變化值,才使得 ObjectAnimator 可以將它應(yīng)用于各個(gè)屬性。
使用方法
我們可以通過(guò) ValueAnimator 的 ofXXX 產(chǎn)生一個(gè) XXX 類型的值(如ofInt),然后為 ValueAnimator 添加一個(gè)更新的回調(diào)事件。在回調(diào)事件中,通過(guò)參數(shù) animation 的 getAnimationValue() 方法,來(lái)獲取對(duì)應(yīng)的 value。有了這個(gè)值,我們就可以實(shí)現(xiàn)我們所有想要的動(dòng)畫(huà)效果。
比如此處就通過(guò) ValueAnimator 實(shí)現(xiàn)了一個(gè)計(jì)時(shí)器的動(dòng)畫(huà)效果。
ValueAnimator animator = ValueAnimator.ofInt(0,100); animator.setDuration(5000); animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Integer value = (Integer) animation.getAnimatedValue(); mButton.setText(""+value); } }); animator.start();
自定義數(shù)值生成器
前面提到,ValueAnimator 可以創(chuàng)建自定義的數(shù)值生成器,做法就是調(diào)用 ValueAnimator 的 ofObject 方法,創(chuàng)建一個(gè) TypeEvaluator 作為參數(shù)。之后我們可以通過(guò)重寫(xiě) TypeEvaluator 的 evaluate 方法,來(lái)按照自己的規(guī)則返回具體的值。
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator() { @Override public Object evaluate(float fraction, Object startValue, Object endValue) { //計(jì)算 return null; //返回值 } });
我們來(lái)看一下 evaluate 方法的幾個(gè)參數(shù)
- float fraction:前面提到的時(shí)間因子
- Object startValue:起始值
- Object endValue:結(jié)束值
通過(guò)這三個(gè)值,我們就可以經(jīng)過(guò)計(jì)算產(chǎn)生所有我們想要的值。
其實(shí),通過(guò) TypeEvaluator,我們不光能產(chǎn)生普通的數(shù)據(jù),還能結(jié)合泛型,我們還能定義更加復(fù)雜的數(shù)據(jù):
我們可以在創(chuàng)建 TypeEvaluator 時(shí)指定具體類型,來(lái)達(dá)到更豐富的效果。比如這里就用到了一個(gè)名為 PointF 的數(shù)據(jù)類型:
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator<PointF>() { @Override public PointF evaluate(float fraction, PointF startValue, PointF endValue) { //計(jì)算 return null; //返回值 } });
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
- Android動(dòng)畫(huà)系列之屬性動(dòng)畫(huà)的基本使用教程
- Android屬性動(dòng)畫(huà)實(shí)現(xiàn)圖片從左到右逐漸消失
- Android利用屬性動(dòng)畫(huà)實(shí)現(xiàn)優(yōu)酷菜單
- Android屬性動(dòng)畫(huà)特點(diǎn)詳解
- Android使用屬性動(dòng)畫(huà)如何自定義倒計(jì)時(shí)控件詳解
- Android屬性動(dòng)畫(huà)之ValueAnimator代碼詳解
- Android 屬性動(dòng)畫(huà)ValueAnimator與插值器詳解
- Android源碼解析之屬性動(dòng)畫(huà)詳解
- Android深入分析屬性動(dòng)畫(huà)源碼
相關(guān)文章
jenkins 遠(yuǎn)程構(gòu)建Android的過(guò)程詳解
這篇文章主要介紹了jenkins 遠(yuǎn)程構(gòu)建Android的過(guò)程詳解的相關(guān)資料,需要的朋友可以參考下2016-09-09使用AndroidStudio上傳忽略文件至SVN Server的解決辦法
這篇文章主要介紹了使用AndroidStudio上傳忽略文件至SVN Server的解決辦法 的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06Android實(shí)現(xiàn)購(gòu)物車功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)購(gòu)物車功能的具體方法 ,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-02-02Android側(cè)邊欄滑動(dòng)切換的view效果
這篇文章主要介紹了Android側(cè)邊欄滑動(dòng)切換的view效果,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03Android string-array數(shù)據(jù)源簡(jiǎn)單使用
這篇文章主要介紹了Android string-array數(shù)據(jù)源簡(jiǎn)單使用的相關(guān)資料,需要的朋友可以參考下2016-09-09android 獲取本機(jī)的IP地址和mac物理地址的實(shí)現(xiàn)方法
本文主要介紹android 獲取本機(jī)的IP地址和mac物理地址的實(shí)現(xiàn)方法,這里提供示例代碼,實(shí)現(xiàn)功能,有需要的小伙伴可以參考下2016-09-09Android自定義下拉刷新控件RefreshableView
這篇文章主要介紹了Android自定義下拉刷新控件RefreshableView,支持所有控件的下拉刷新,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Android實(shí)現(xiàn)調(diào)用攝像頭拍照與視頻功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)調(diào)用攝像頭拍照與視頻功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04Android類加載ClassLoader雙親委托機(jī)制詳解
這篇文章主要為大家介紹了Android類加載ClassLoader雙親委托機(jī)制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07