中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android動畫教程之屬性動畫詳解

發布時間:2020-10-15 22:45:17 來源:腳本之家 閱讀:136 作者:被被的安卓編程之路 欄目:移動開發

簡介

Android 開發中,總是需要一些動畫來優化用戶的交互體驗,提高用戶滿意度。因此,Google 為我們提供了一些用于處理動畫效果的動畫框架。Android 的動畫框架分為兩類:

  • 傳統動畫(Animation):通過系統不斷調用onDraw方法重繪界面,來達到動畫的效果。
  • 屬性動畫(Animator):通過操縱一個屬性的get/set方法,真實地改變目標的某些屬性。

傳統動畫框架的局限性

既然有了傳統動畫框架,Google 為什么還要創造一個屬性動畫框架呢?

我們下面舉個例子來說明一下傳統動畫的局限性。

在布局中加入一個 ImageView 和一個 Button,點擊 ImageView 后彈出一個 Toast,點擊 Button 后使 ImageView 展現一個向右平移的動畫效果。

下面是使用傳統動畫實現的代碼:

TranslateAnimation animation = new TranslateAnimation(0,200,0,0); // 平移動畫x軸移動200,y軸不動
animation.setDuration(1000); // 動畫時長
animation.setFillAfter(true); // 使動畫結束后停留在結束的位置
mIvPicture.startAnimation(animation);

運行后,ImageView 確實進行了我們預期的平移的效果。可是當我們嘗試點擊 ImageView 當前的位置時,卻沒有 Toast 彈出。我們再嘗試去點擊 ImageView 開始動畫前的位置,卻成功彈出了 Toast。

這就是傳統動畫很大的局限性:

  • 它僅僅是重繪了控件,改變了其顯示的位置。但真正事件響應的位置,卻并沒有發生改變。因此傳統動畫不適合做具有交互的動畫效果。僅僅能做一些顯示的動畫效果。
  • 傳統動畫是不斷通過 onDraw() 方法重繪界面,必然會十分耗費GPU資源。
  • 傳統動畫所支持的動畫類型少,僅有旋轉、縮放、位移、透明度這四種動畫效果。雖然通過組合可以實現豐富的效果,但相比直接通過改變屬性來實現的屬性動畫來說,還是有很大的局限性的。

因此,Google 為我們提供了一套全新的屬性動畫框架,來讓我們實現更豐富的動畫效果。

ObjectAnimator

ObjectAnimator 是屬性動畫中,最簡單也最常用的一個對象。

實現 Animation 框架的功能

平移

前文提到的使 ImageVIew 向右平移 200 像素的動畫效果,使用屬性動畫只需要很簡單的幾句代碼即可實現:

ObjectAnimator.ofFloat(mIvPicture,"translationX",0F,200F)
 .setDuration(1000)
 .start();

我們來分析一下這一句代碼。我們調用了ofFloat代碼,并傳入三個參數。

​ 第一個參數是動畫需要操縱的目標,在這里是我們的 ImageView。

​ 第二個參數是所需要操縱的目標所具備的屬性名稱。

​ 第三個參數是這個動畫變化的取值范圍。

最后設置一下它的動畫的屬性,便可以 start 了。

這次我們再次點擊 ImageView 目前的位置,成功地彈出了 Toast。這證實了屬性動畫是通過改變物體的屬性來達到動畫效果的理論。

當我們需要改變 y 坐標時,只需要把 "translationX" 變為 "translationY" 即可。

其實 ,只要Google對一個對象的某個屬性提供了get和set方法,我們就可以使用這個屬性來實現動畫效果。

其實我們還能用 X Y 兩個屬性實現之前的動畫效果,那么對象屬性中 X 的 Y 與 translationX translationY 有什么區別呢?

translationX translationY指的是物體的偏移量,而X Y則表示它最終到達的絕對位置。

旋轉

旋轉屬性使用的是 "rotation" 屬性,后面的變換范圍的單位是角度。

比如想讓 ImageView 旋轉90度,只需要

ObjectAnimator.ofFloat(mIvPicture,"rotation",0F,90F)
 .setDuration(1000)
 .start();

其他

其實屬性動畫能操縱的屬性,只要具有 set、get 方法,都可以進行操縱。如 scaleX、scaleY 等等...

插值器

Android 為我們內置了插值器,使我們的動畫更為自然。比如可以讓我們的平移動畫像物體的重力加速度由快到慢的 Accelerate 等等

Android中內置了七種插值器,分別是

  • Accelerate
  • Decelerate
  • Accelerate/Decelerate
  • Anticipate
  • Overshoot
  • Anticipate/Overshoot
  • Bounce

要應用插值器,可以調用 ObjectAnimator 的 setInterpolator 方法, new 出對應的插值器作為參數(xxxInterpolator)。比如下面這段代碼:

animator.setInterpolator(new AccelerateInterpolator());

通過插值器,我們可以讓動畫的效果更佳自然。

多種屬性動畫同時作用

當我們把幾種動畫按順序寫下時,運行程序,會發現效果是三種屬性動畫的疊加。由此可以發現,屬性動畫在調用 start 方法后,實際上是一個異步的過程。因此我們就可以看到三個屬性動畫同時作用的效果。通過這樣的方法,其實就可以實現多種屬性動畫同時作用的效果:

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();

其實 Google 為我們提供了更好的方法,來實現這樣的效果。

我們可以使用 PropertyValuesHolder 來實現。其構造函數僅僅比 ObjectAnimator 少了一個作用對象參數。之后通過ObjectAnimator 的 ofPropertyValuesHolder 方法,傳入作用對象以及要同時作用的 PropertyValuesHolder 即可執行。可以看到下面的代碼示例:

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();

運行后可以發現,與之前的效果是相同的。

那既然兩種方法效果一樣,這樣相比之前有什么好處么?

  • 其實 Google 在 PropertyValuesHolder 內部進行了一些優化,使得我們使用多個屬性動畫時更加有效率,節省系統資源。

AnimatorSet 屬性集合

playTogether 方法

我們其實還可以通過 AnimatorSet,來實現同樣的效果。這里我們調用了 set 的 playTogether 方法,使得這些方法同時執行:

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 方法,它可以使得動畫按順序執行。具體順序取決于調用時的參數順序。

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 方法

我們除了可以用上述方法來讓動畫按順序執行外,也可以通過 AnimatorSet 的 play、with、after、before 等方法相組合來控制動畫播放關系。

例如如下的代碼就可以實現先平移,再旋轉的效果

set.play(animator1).with(animator3);
set.play(animator2).after(animator1);

動畫監聽事件

通過下面的代碼,我們可以實現按鈕按下后漸隱的效果。

mBtnPress.setOnClickListener(new OnClickListener() {
 @Override
 public void onClick(View v) {
  ObjectAnimator animator = ObjectAnimator.ofFloat(mBtnPress,"alpha",1F,0F);
  animator.setDuration(1000);
  animator.start();
 }
});

但如果我們想要在動畫播放完成后再執行一些操作的話,又該如何實現呢?

  • 我們可以使用 ObjectAnimator 的 addListener方法,傳入一個AnimatorListener,為動畫設置監聽事件。

AnimatorListener

一個AnimatorListener,需要實現四個方法,分別是:

  • onAnimationStart
  • onAnimationEnd
  • onAnimationCancel
  • onAnimationRepeat

它們的回調時機我們根據字面意思便可以理解。大部分時候,我們需要實現的是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

如果每次監聽都需要實現這么多方法,未免太麻煩了一點。因此 Android 為我們提供了另一種方法來添加動畫的監聽事件:在添加 AnimatorListener 的時候,傳入 AnimatorListenerAdapter 即可。這樣我們就只需要實現自己需要的方法即可。

animator.addListener(new AnimatorListenerAdapter() {
 @Override
 public void onAnimationEnd(Animator animation) {
  super.onAnimationEnd(animation);
  Toast.makeText(MainActivity.this,"Animation End",Toast.LENGTH_SHORT).show();
 }
});

ValueAnimator

簡介

ValueAnimator 本身不作用于任何一個屬性,也不提供任何一種動畫。它就是一個數值發生器,可以產生想要的各種數值。Android 系統為它提供了很多計算數值的方法,如 int、float 等等。我們也可以自己實現計算數值的方法。其實,在屬性動畫中,如何產生每一步的動畫效果,都是通過 ValueAnimator 計算出來的。

比如我們要實現一個從 0-100 的位移動畫。隨著動畫時間的持續,它產生的值也會從 0-100 遞增。通過這個 ValueAnimator 產生的值,再進行屬性的設置即可。

那么 ValueAnimator 究竟是如何產生這些值的呢?

  • 首先 ValueAnimator會根據會根據動畫已進行的時間與它持續的總時間的比值,產生一個0-1的時間因子。有了這樣的時間因子,經過相應的變換,就可以根據初始值和最終值來生成中間的相應值。同時,通過插值器的使用,我們還可以進一步控制每一個時間因子產生值的變化速率。如果我們使用的是線性插值器,那么它生成值的時候就會呈一個線性變化。如果我們使用一個加速度插值器,那么它生成值時便會呈一個二次曲線,增長率越來越快。

由于 ValueAnimator 不作用于任何一個屬性,也不提供任何一種動畫。因此并沒有 ObjectAnimator 使用得廣泛。
實際上,ObjectAnimator 就是基于 ValueAnimator 進行的一次封裝。我們可以查看 ObjectAnimator 的源碼,會發現它繼承自 ValueAnimator,是它的一個子類。正是 ValueAnimator 產生的變化值,才使得 ObjectAnimator 可以將它應用于各個屬性。

使用方法

我們可以通過 ValueAnimator 的 ofXXX 產生一個 XXX 類型的值(如ofInt),然后為 ValueAnimator 添加一個更新的回調事件。在回調事件中,通過參數 animation 的 getAnimationValue() 方法,來獲取對應的 value。有了這個值,我們就可以實現我們所有想要的動畫效果。

比如此處就通過 ValueAnimator 實現了一個計時器的動畫效果。

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();

自定義數值生成器

前面提到,ValueAnimator 可以創建自定義的數值生成器,做法就是調用 ValueAnimator 的 ofObject 方法,創建一個 TypeEvaluator 作為參數。之后我們可以通過重寫 TypeEvaluator 的 evaluate 方法,來按照自己的規則返回具體的值。

ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator() {
 @Override
 public Object evaluate(float fraction, Object startValue, Object endValue) {
  //計算
  return null; //返回值
 }
});

我們來看一下 evaluate 方法的幾個參數

  • float fraction:前面提到的時間因子
  • Object startValue:起始值
  • Object endValue:結束值

通過這三個值,我們就可以經過計算產生所有我們想要的值。

其實,通過 TypeEvaluator,我們不光能產生普通的數據,還能結合泛型,我們還能定義更加復雜的數據:

我們可以在創建 TypeEvaluator 時指定具體類型,來達到更豐富的效果。比如這里就用到了一個名為 PointF 的數據類型:

ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator<PointF>() {
 @Override
 public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
  //計算
  return null; //返回值
 }
});

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

承德县| 本溪| 湾仔区| 通海县| 德阳市| 蒙自县| 古丈县| 中超| 武汉市| 榆社县| 冷水江市| 卓尼县| 昌乐县| 墨脱县| 红安县| 西安市| 赣州市| 弥渡县| 雅安市| 彭阳县| 玛纳斯县| 扶余县| 集贤县| 兖州市| 东源县| 栾川县| 确山县| 牡丹江市| 七台河市| 正宁县| 石阡县| 鄯善县| 新蔡县| 博野县| 泊头市| 皮山县| 阳西县| 石柱| 通州市| 拉萨市| 黄骅市|