Android動畫的理解

cryAllen發表於2016-11-14

基礎知識

在我們開始講Android動畫這個知識點之前,我們瞭解下相應的基礎知識點。

Shape篇

一般用Shape定義的XML檔案是存放在Drawable目錄下,廣泛應用於在Button、TextView、EditText等各種基本控制元件中,來實現不同背景、顏色、邊框。

使用Shape可以自定義形狀,可以定義下面四種型別的形狀,通過android:shape屬性指定:

  • rectangle:矩形,預設的形狀
  • oval:橢圓形,可以畫正圓
  • line:線形,可以畫實線和虛線
  • ring:環形,可以話進度條之類

rectange

  • solid: 設定形狀填充的顏色,只有android:color一個屬性
    • android:color 填充的顏色
  • padding: 設定內容與形狀邊界的內間距,可分別設定左右上下的距離
    • android:left 左內間距
    • android:right 右內間距
    • android:top 上內間距
    • android:bottom 下內間距
  • gradient: 設定形狀的漸變顏色,可以是線性漸變、輻射漸變、掃描性漸變
    • android:type 漸變的型別linear 線性漸變,預設的漸變型別radial 放射漸變,設定該項時,android:gradientRadius也必須設定sweep 掃描性漸變
    • android:startColor 漸變開始的顏色
    • android:endColor 漸變結束的顏色
    • android:centerColor 漸變中間的顏色
    • android:angle 漸變的角度,線性漸變時才有效,必須是45的倍數,0表示從左到右,90表示從下到上
    • android:centerX 漸變中心的相對X座標,放射漸變時才有效,在0.0到1.0之間,預設為0.5,表示在正中間
    • android:centerY 漸變中心的相對X座標,放射漸變時才有效,在0.0到1.0之間,預設為0.5,表示在正中間
    • android:gradientRadius 漸變的半徑,只有漸變型別為radial時才使用
    • android:useLevel 如果為true,則可在LevelListDrawable中使用
  • corners: 設定圓角,只適用於rectangle型別,可分別設定四個角不同半徑的圓角,當設定的圓角半徑很大時,比如200dp,就可變成弧形邊了
    • android:radius 圓角半徑,會被下面每個特定的圓角屬性重寫
    • android:topLeftRadius 左上角的半徑
    • android:topRightRadius 右上角的半徑
    • android:bottomLeftRadius 左下角的半徑
    • android:bottomRightRadius 右下角的半徑
  • stroke: 設定描邊,可描成實線或虛線。
    • android:color 描邊的顏色
    • android:width 描邊的寬度
    • android:dashWidth 設定虛線時的橫線長度
    • android:dashGap 設定虛線時的橫線之間的距離

oval

它也有solid、padding、stroke、gradient、size幾個特性,size是用來設定形狀大小的,如下:

size: 設定形狀預設的大小,可設定寬度和高度

android:width 寬度

android:height 高度

line

畫線時,有幾點特性必須要知道的:

  1. 只能畫水平線,畫不了豎線;
  2. 線的高度是通過stroke的android:width屬性設定的;
  3. size的android:height屬性定義的是整個形狀區域的高度;
  4. size的height必須大於stroke的width,否則,線無法顯示;
  5. 線在整個形狀區域中是居中顯示的;
  6. 線左右兩邊會留有空白間距,線越粗,空白越大;
  7. 引用虛線的view需要新增屬性android:layerType,值設為"software",否則顯示不了虛線。

ring

首先,shape根元素有些屬性只適用於ring型別,先過目下這些屬性吧:

  • android:innerRadius 內環的半徑
  • android:innerRadiusRatio 浮點型,以環的寬度比率來表示內環的半徑,預設為3,表示內環半徑為環的寬度除以3,該值會被android:innerRadius覆蓋
  • android:thickness 環的厚度
  • android:thicknessRatio 浮點型,以環的寬度比率來表示環的厚度,預設為9,表示環的厚度為環的寬度除以9,該值會被android:thickness覆蓋
  • android:useLevel 一般為false,否則可能環形無法顯示,只有作為LevelListDrawable使用時才設為true
Selector篇

shape只能定義單一的形狀,而實際應用中,很多地方比如按鈕、Tab、ListItem等都是不同狀態有不同的展示形狀。舉個例子,一個按鈕的背景,預設時是一個形狀,按下時是一個形狀,不可操作時又是另一個形狀。有時候,不同狀態下改變的不只是背景、圖片等,文字顏色也會相應改變。而要處理這些不同狀態下展示什麼的問題,就要用selector來實現了。

那麼,看看都有哪些狀態可以設定呢:

  • android:state_enabled: 設定觸控或點選事件是否可用狀態,一般只在false時設定該屬性,表示不可用狀態
  • android:state_pressed: 設定是否按壓狀態,一般在true時設定該屬性,表示已按壓狀態,預設為false
  • android:state_selected: 設定是否選中狀態,true表示已選中,false表示未選中
  • android:state_checked: 設定是否勾選狀態,主要用於CheckBox和RadioButton,true表示已被勾選,false表示未被勾選
  • android:state_checkable: 設定勾選是否可用狀態,類似state_enabled,只是state_enabled會影響觸控或點選事件,而state_checkable影響勾選事件
  • android:state_focused: 設定是否獲得焦點狀態,true表示獲得焦點,預設為false,表示未獲得焦點
  • android:state_window_focused: 設定當前視窗是否獲得焦點狀態,true表示獲得焦點,false表示未獲得焦點,例如拉下通知欄或彈出對話方塊時,當前介面就會失去焦點;另外,ListView的ListItem獲得焦點時也會觸發true狀態,可以理解為當前視窗就是ListItem本身
  • android:state_activated: 設定是否被啟用狀態,true表示被啟用,false表示未啟用,API Level 11及以上才支援,可通過程式碼呼叫控制元件的setActivated(boolean)方法設定是否啟用該控制元件
  • android:state_hovered: 設定是否滑鼠在上面滑動的狀態,true表示滑鼠在上面滑動,預設為false,API Level 14及以上才支援
Layer-List篇

使用layer-list可以將多個drawable按照順序層疊在一起顯示。

帶陰影的圓角矩形

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 灰色陰影 -->
    <item
        android:left="2dp"
        android:top="4dp">
        <shape>
            <solid android:color="@android:color/darker_gray" />
            <corners android:radius="10dp" />
        </shape>
    </item>
    <!-- 白色前景 -->
    <item
        android:bottom="4dp"
        android:right="2dp">
        <shape>
            <solid android:color="#FFFFFF" />
            <corners android:radius="10dp" />
        </shape>
    </item>
</layer-list>

從上面的示例程式碼可以看到,layer-list可以作為根節點,也可以作為selector中item的子節點。layer-list可以新增多個item子節點,每個item子節點對應一個drawable資源,按照item從上到下的順序疊加在一起,再通過設定每個item的偏移量就可以看到陰影等效果了。layer-list的item可以通過下面四個屬性設定偏移量:

  • android:top 頂部的偏移量
  • android:bottom 底部的偏移量
  • android:left 左邊的偏移量
  • android:right 右邊的偏移量

這四個偏移量和控制元件的margin設定差不多,都是外間距的效果。如何不設定偏移量,前面的圖層就完全擋住了後面的圖層,從而也看不到後面的圖層效果了。

另外,關於item的用法,也做下總結:

  1. 根節點不同時,可設定的屬性是會不同的,比如selector下,可以設定一些狀態屬性,而在layer-list下,可以設定偏移量;
  2. 就算父節點同樣是selector,放在drawable目錄和放在color目錄下可用的屬性也會不同,比如drawable目錄下可用的屬性為android:drawable,在color目錄下可用的屬性為android:color;
  3. item的子節點可以為任何型別的drawable類標籤,除了上面例子中的shape、color、layer-list,也可以是selector,還有其他沒講過的bitmap、clip、scale、inset、transition、rotate、animated-rotate、lever-list等等。
Drawable資源篇

nine-patch標籤

使用nine-patch標籤可以對點九圖片做一些設定處理,不過可設定的屬性並不多:

  • android:src 必填項,必須指定點九型別的圖片
  • android:dither 設定是否抖動,圖片與螢幕的畫素配置不同時會用到,比如圖片是ARGB 8888的,而螢幕是RGB565
  • android:tint 給圖片著色,比如圖片本來是黑色的,著色後可以變成白色
  • android:tintMode 著色模式,API Level 21(Android 5.0)才新增的屬性
  • android:alpha 設定圖片的透明度,取值範圍為0.0~1.0之間,0.0為全透明,1.0為全不透明,API Level最低要求是11
  • android:autoMirrored 設定圖片是否需要映象反轉,當佈局方向是RTL,即從右到左佈局時才有用,API Level 19(Android 4.4)才新增的屬性

scale標籤

使用scale標籤可以對drawable進行縮放操作,和clip一樣是通過設定level來控制縮放的比例。scale標籤可以設定的屬性如下:

  • android:drawable 指定drawable資源,如果不設定該屬性,也可以定義drawable型別的子標籤
  • android:scaleHeight 設定可縮放的高度,用百分比表示,格式為XX%,0%表示不做任何縮放,50%表示只能縮放一半
  • android:scaleWidth 設定可縮放的寬度,用百分比表示,格式為XX%,0%表示不做任何縮放,50%表示只能縮放一半
  • android:scaleGravity 設定drawable縮放後的位置,取值和bitmap標籤的一樣,就不一一列舉說明了,不過預設值是left
  • android:useIntrinsicSizeAsMinimum 設定drawable原有尺寸作為最小尺寸,設為true時,縮放基本無效,API Level最低要求為11

rotate標籤

使用rotate標籤可以對一個drawable進行旋轉操作,在shape篇講環形時最後舉了個進度條時就用到了rotate標籤。另外,比如你有一張箭頭向上的圖片,但你還需要一個箭頭向下的圖片,這時就可以使用rotate將向上的箭頭旋轉變成一張箭頭向下的drawable。

先看看rotate標籤的一些屬性吧:

  • android:drawable 指定drawable資源,如果不設定該屬性,也可以定義drawable型別的子標籤
  • android:fromDegrees 起始的角度度數
  • android:toDegrees 結束的角度度數,正數表示順時針,負數表示逆時針
  • android:pivotX 旋轉中心的X座標,浮點數或是百分比。浮點數表示相對於drawable的左邊緣距離

Drawable Animation

引用官方文件的話

Drawable animation lets you load a series of Drawable resources one after another to create an animation. This is a traditional animation in the sense that it is created with a sequence of different images, played in order, like a roll of film.

其實說明動畫的Drawable的資源按照一定順序執行,最終出來的效果類似電影中一幀一幀的,它允許你實現像播放幻燈片一樣的效果。

是它的根元素,做為根節點,下面就是一個例子

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>

這裡定義了三個drawable資原始檔,每次執行時間為200ms,順序是從上到下,這種動畫最常見應用場景是在想表達電池充電的過程,從0%,25%,50%,100%表達連續充電的過程,就可以做四個不同的資源圖片,然後按照一定的順序,進而執行相應的執行時間。

有了動畫檔案之後,具體怎麼用呢,如下:

AnimationDrawable rocketAnimation;
 
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
 
  ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
  rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
  rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
} 
 
public boolean onTouchEvent(MotionEvent event) {
  if (event.getAction() == MotionEvent.ACTION_DOWN) {
    rocketAnimation.start();
    return true; 
  } 
  return super.onTouchEvent(event);
} 

特別注意,AnimationDrawable的start()方法不能在Activity的onCreate方法中調運,因為AnimationDrawable還未完全附著到window上,所以最好的調運時機是onWindowFocusChanged()方法中。

其中Drawable Animation還有一個animated-rotate屬性來定義旋轉動畫,這個用法也是類似的,不再過多講述。

View Animation

引用官方文件

You can use the view animation system to perform tweened animation on Views. Tween animation calculates the animation with information such as the start point, end point, size, rotation, and other common aspects of an animation.

檢視動畫,也叫Tween(補間)動畫可以在一個檢視容器內執行一系列簡單變換(位置、大小、旋轉、透明度)。譬如,如果你有一個TextView物件,您可以移動、旋轉、縮放、透明度設定其文字,當然,如果它有一個背景影象,背景影象會隨著文字變化。

補間動畫通過XML或Android程式碼定義,建議使用XML檔案定義,因為它更具可讀性、可重用性。

Animation控制的是整個View,實現的原理是每次繪製檢視時,View所在的ViewGroup的drawChild函式獲取該View的Animation的Transformation值,然後呼叫canvas.concat(transformToApply.getMatrix()),通過矩陣運算完成動畫幀。如果動畫沒完成,就繼續呼叫invalidate()函式,啟動下次繪製來驅動動畫,從而完成整個動畫的繪製。

檢視動畫使用簡單,主要有以下四種型別動畫:

  • Alpha Animation
  • Rotate Animation
  • Translate Animation
  • Scale Animation

並提供了AnimationSet動畫集合,混合使用多種動畫方式,在Android 3.0之前,View Animation一家獨大,但是隨著Property Animation的推出,風光就大不如前,相比屬性動畫,檢視動畫一個最大的缺陷是不具備互動性,當某個元素髮生檢視動畫後,其響應的事件的位置還依然在動畫前的地方,所以檢視動畫只能做普通的動畫效果,避免互動的發生,但它的優點是,效率高而且使用方便。

Alpha Animation

可以實現透明度漸變的動畫效果,也就是淡入淡出的效果,可通過設定下面三個屬性來設定淡入或淡出效果:

  • android:duration 動畫從開始到結束持續的時長,單位為毫秒
  • android:fromAlpha 動畫開始時的透明度,0.0為全透明,1.0為不透明,預設為1.0
  • android:toAlpha 動畫結束時的透明度,0.0為全透明,1.0為不透明,預設為1.0

當設定開始時透明度為0.0,結束時為1.0,就能實現淡入效果;相反,當設定開始時透明度為1.0,結束時為0.0,那就能實現淡出效果。示例程式碼如下:

<!-- res/anim/fade_in.xml -->
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromAlpha="0.0"
    android:toAlpha="1.0" />

將這動畫效果新增到View上也只需要一行程式碼:

view.startAnimation(AnimationUtils.loadAnimation(this, R.anim.fade_in));

將這動畫效果新增到View上也只需要一行程式碼:

Rotate Animation

可以實現旋轉的動畫效果,主要的屬性如下:

  • android:duration 動畫從開始到結束持續的時長,單位為毫秒
  • android:fromDegrees 旋轉開始的角度
  • android:toDegrees 旋轉結束的角度
  • android:pivotX 旋轉中心點的X座標,純數字表示相對於View本身左邊緣的畫素偏移量;帶"%"字尾時表示相對於View本身左邊緣的百分比偏移量;帶"%p"字尾時表示相對於父View左邊緣的百分比偏移量
  • android:pivotY 旋轉中心點的Y座標,純數字表示相對於View本身頂部邊緣的畫素偏移量;帶"%"字尾時表示相對於View本身頂部邊緣的百分比偏移量;帶"%p"字尾時表示相對於父View頂部邊緣的百分比偏移量

以下示例程式碼旋轉角度從0到360,即旋轉了一圈,旋轉的中心點都設為了50%,即是View本身中點的位置。

<!-- res/anim/rotate_one.xml -->
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromDegrees="0"
    android:toDegrees="360"
    android:pivotX="50%"
    android:pivotY="50%" />

標籤對應的類為RotateAnimation,父類也是Animation,新增到View上的程式碼如下:

RotateAnimation rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(this, R.anim.rotate_one);
view.startAnimation(rotateAnimation);
Translate Animation

可以實現位置移動的動畫效果,可以是垂直方向的移動,也可以是水平方向的移動。座標的值可以有三種格式:從-100到100,以"%"結束,表示相對於View本身的百分比位置;如果以"%p"結束,表示相對於View的父View的百分比位置;如果沒有任何字尾,表示相對於View本身具體的畫素值。主要的屬性如下:

  • android:duration 動畫從開始到結束持續的時長,單位為毫秒
  • android:fromXDelta 起始位置的X座標的偏移量
  • android:toXDelta 結束位置的X座標的偏移量
  • android:fromYDelta 起始位置的Y座標的偏移量
  • android:toYDelta 結束位置的Y座標的偏移量

看示例吧,以下程式碼實現的是從左到右的移動效果,起始位置為相對於控制元件本身-100%的位置,即在控制元件左邊,與控制元件本身寬度一致的位置;結束位置為相對於父控制元件100%的位置,即會移出父控制元件右邊緣的位置。

<!-- res/anim/move_left_to_right.xml -->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromXDelta="-100%"
    android:fromYDelta="0"
    android:toXDelta="100%p"
    android:toYDelta="0" />

標籤對應的類為TranslateAnimation,父類也是Animation,新增到View上的程式碼如下:

TranslateAnimation moveAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this, R.anim.move_left_to_right);
view.startAnimation(moveAnimation);
Scale Animation

可以實現縮放的動畫效果,主要的屬性如下:

  • android:duration 動畫從開始到結束持續的時長,單位為毫秒
  • android:fromXScale 動畫開始時X座標上的縮放尺寸
  • android:toXScale 動畫結束時X座標上的縮放尺寸
  • android:fromYScale 動畫開始時Y座標上的縮放尺寸
  • android:toYScale 動畫結束時Y座標上的縮放尺寸
    PS以上四個屬性,0.0表示縮放到沒有,1.0表示正常無縮放,小於1.0表示收縮,大於1.0表示放大
  • android:pivotX 縮放時的固定不變的X座標,一般用百分比表示,0%表示左邊緣,100%表示右邊緣
  • android:pivotY 縮放時的固定不變的Y座標,一般用百分比表示,0%表示頂部邊緣,100%表示底部邊緣

具體示例:

<!-- res/anim/zoom_out.xml -->
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:pivotX="0%"
    android:pivotY="100%"
    android:toXScale="1.5"
    android:toYScale="1.5" />

對應的類為ScaleAnimation,父類也是Animation,新增到View上的用法和AlphaAnimation一樣,程式碼如下:

ScaleAnimation zoomOutAnimation = (ScaleAnimation) AnimationUtils.loadAnimation(this, R.anim.zoom_out);
view.startAnimation(zoomOutAnimation);
Set Animation

標籤可以將多個動畫組合起來,變成一個動畫集。比如想將一張圖片縮放的同時也做移動,這時候就要用set標籤組合縮放動畫和移動動畫了。示例程式碼如下:

<!-- res/anim/move_and_scale.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000">
    <translate
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="200%"
        android:toYDelta="0" />
    <scale
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="0%"
        android:pivotY="100%"
        android:toXScale="1.5"
        android:toYScale="1.5" />
</set>

以上程式碼實現的動畫效果為向右移動的同時也同步放大。Set標籤在檢視動畫中除了可以組合Alpha ,Rotate , Scale , Scale 這四種標籤,也可以巢狀其他Set標籤。

Property Animation

引用官方文件

The property animation system is a robust framework that allows you to animate almost anything. You can define an animation to change any object property over time, regardless of whether it draws to the screen or not. A property animation changes a property's (a field in an object) value over a specified length of time. To animate something, you specify the object property that you want to animate, such as an object's position on the screen, how long you want to animate it for, and what values you want to animate between

檢視動畫只能作用於View,而且檢視動畫改變的只是View的繪製效果,View真正的屬性並沒有改變。比如,一個按鈕做平移的動畫,雖然按鈕的確做了平移,但按鈕可點選的區域並沒隨著平移而改變,還是在原來的位置。而屬性動畫則可以改變真正的屬性,從而實現按鈕平移時點選區域也跟著平移。通俗點說,屬性動畫其實就是在一定時間內,按照一定規律來改變物件的屬性,從而使物件展現出動畫效果。

屬性動畫是在android 3.0引入的動畫體系,如果還想適配基本已經滅絕的2.x版本,只好繞道了。
屬性動畫和檢視動畫一樣,可以通過xml檔案定義,不同的是,檢視動畫的xml檔案放於res/anim/目錄下,而屬性動畫的xml檔案則放於res/animator/目錄下。一個是anim,一個是animator,別搞錯了。同樣的,在Java程式碼裡引用屬性動畫的xml檔案時,則用R.animator.filename,不同於檢視動畫,引用時為R.anim.filename

屬性動畫主要有三個元素:animator,objectAnimator,set

相對應的有三個類:ValueAnimatorObjectAnimatorAnimatorSet
ValueAnimator是基本的動畫類,處理值動畫,通過監聽某一值的變化,進行相應的操作。ObjectAnimatorValueAnimator的子類,處理物件動畫。AnimatorSet則為動畫集,可以組合另外兩種動畫或動畫集。相應的三個標籤元素的關係也一樣。
樣式開發主要還是用xml的形式,所以這裡先主要還是講標籤的用法。

animator

animator標籤與對應的ValueAnimator類提供了屬性動畫的核心功能,包括計算動畫值、動畫時間細節、是否重複等。執行屬性動畫分兩個步驟:

  1. 計算動畫值
  2. 將動畫值應用到物件和屬性上

ValuAnimiator只完成第一步,即只計算值,要實現第二步則需要在值變化的監聽器裡自行更新物件屬性。
通過animator標籤可以很方便的對ValuAnimiator進行設定,可設定的屬性如下:

  • android:duration 動畫從開始到結束持續的時長,單位為毫秒
  • android:startOffset 設定動畫執行之前的等待時長,單位為毫秒
  • android:repeatCount 設定動畫重複執行的次數,預設為0,即不重複;可設為-1或infinite,表示無限重複
  • android:repeatMode 設定動畫重複執行的模式,可設為以下兩個值其中之一:
    • restart 動畫重複執行時從起點開始,預設為該值
    • reverse 動畫會反方向執行
  • android:valueFrom 動畫開始的值,可以為int值、float值或color值
  • android:valueTo 動畫結束的值,可以為int值、float值或color值
  • android:valueType 動畫值型別,若為color值,則無需設定該屬性
    • intType 指定動畫值,即以上兩個value屬性的值為整型
    • floatType 指定動畫值,即以上兩個value屬性的值為浮點型,預設值
  • android:interpolator 設定動畫速率的變化,比如加速、減速、勻速等,需要指定Interpolator資源。

接著,用一個例項講解具體的用法吧。在這個例子裡,將一個按鈕的寬度進行縮放,從100%縮放到20%。
xml檔案的程式碼如下:

<!-- res/animator/value_animator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:valueFrom="100"
    android:valueTo="20"
    android:valueType="intType" />

可看到,值的變化從100到20,動畫時長3000毫秒,以下則是目標按鈕的xml程式碼:

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/bg_btn_normal"
    android:onClick="onScaleWidth"
    android:text="點我"
    android:textColor="@android:color/white" />

按鈕預設是填充螢幕寬度的,點選時的執行方法為onScaleWidth,以下則是onScaleWidth方法的程式碼:

public void onScaleWidth(final View view) {
    // 獲取螢幕寬度
    final int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
    ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.value_animator);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animator) {
            // 當前動畫值,即為當前寬度比例值
            int currentValue = (Integer) animator.getAnimatedValue();
            // 根據比例更改目標view的寬度
            view.getLayoutParams().width = maxWidth * currentValue / 100;
            view.requestLayout();
        }
    });
    valueAnimator.start();
}

從中,我們可以看到屬性動畫則是通過AnimatorInflater類的loadAnimation()方法獲取相應的Animator類例項。另外,ValueAnimator通過新增AnimatorUpdateListener監聽器監聽值的變化,從而再手動更新目標物件的屬性。最後,通過呼叫valueAnimator.start()方法啟動動畫。

objectAnimator

objectAnimator標籤對應的類為ObjectAnimator,為ValueAnimator的子類。objectAnimator標籤與標籤animator不同的是,可以直接指定動畫的目標物件的屬性。標籤可設定的屬性除了和一樣的那些,另外多了一個:

  • android:propertyName 目標物件的屬性名,要求目標物件必須提供該屬性的setter方法,如果動畫的時候沒有初始值,還需要提供getter方法

還是用例項說明具體用法,還是用上面的例子,將一個按鈕的寬度進行縮放,從100%縮放到20%,但這次改用objectAnimator實現。

<!-- res/animator/object_animator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:propertyName="width"
    android:valueFrom="100"
    android:valueTo="20"
    android:valueType="intType" />

與上面animator標籤方式做對比,就只是多了一個android:propertyName的屬性,設定值為width。也就是說,動畫改變的屬性為width,值將從100逐漸減到20。另外,值是從setWidth()傳遞過去的,再從getWidth()獲取。而且,這裡設定的值代表的是比例值,因此,還需要進行計算轉化為實際的寬度值。最後,物件實際的寬度值為view.getLayoutParams().width。因此,需要用一個包裝類來包裝原始的view物件,對其提供setWidth()和getWidth()方法,程式碼如下:

private static class ViewWrapper {
    private View target; //目標物件
    private int maxWidth; //最長寬度值

    public ViewWrapper(View target, int maxWidth) {
        this.target = target;
        this.maxWidth = maxWidth;
    }

    public int getWidth() {
        return target.getLayoutParams().width;
    }

    public void setWidth(int widthValue) {
        //widthValue的值從100到20變化
        target.getLayoutParams().width = maxWidth * widthValue / 100;
        target.requestLayout();
    }
}

上面setWidth()的程式碼裡,根據比例值轉化為了實際的寬度值。最後,動畫處理的程式碼如下:

public void onScaleWidth(View view) {
    // 獲取螢幕寬度
    int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
    // 將目標view進行包裝
    ViewWrapper wrapper = new ViewWrapper(view, maxWidth);
    // 將xml轉化為ObjectAnimator物件
    ObjectAnimator objectAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.object_animator);
    // 設定動畫的目標物件為包裝後的view
    objectAnimator.setTarget(wrapper);
    // 啟動動畫
    objectAnimator.start();
}

ObjectAnimator提供了屬性的設定,但相應的需要有該屬性的setter和getter方法。而ValueAnimator則只是定義了值的變化,並不指定目標屬性,所以也不需要提供setter和getter方法,但只能在AnimatorUpdateListener監聽器裡手動更新屬性。不過,也因為沒有指定屬性,所以其實更具靈活性了,你可以在監聽器里根據值的變化做任何事情,比如更新多個屬性,比如在縮放寬度的同時做垂直移動。

為了對View更方便的設定屬性動畫,Android系統也提供了View的一些屬性和相應的setter和getter方法:

  • alpha:透明度,預設為1,表示不透明,0表示完全透明
  • pivotX 和 pivotY:旋轉的軸點和縮放的基準點,預設是View的中心點
  • scaleX 和 scaleY:基於pivotX和pivotY的縮放,1表示無縮放,小於1表示收縮,大於1則放大
  • rotationrotationX 和 rotationY:基於軸點(pivotX和pivotY)的旋轉,rotation為平面的旋轉,rotationX和rotationY為立體的旋轉
  • translationX 和 translationY:View的螢幕位置座標變化量,以layout容器的左上角為座標原點
  • x 和 y:View在父容器內的最終位置,是左上角座標和偏移量(translationX,translationY)的和
set

set標籤對應於AnimatorSet類,可以將多個動畫組合成一個動畫集,如上面提到的在縮放寬度的同時做垂直移動,可以將一個縮放寬度的動畫和一個垂直移動的動畫組合在一起。
set標籤有一個屬性可以設定動畫的時序關係:

  • android:ordering 設定動畫的時序關係,取值可為以下兩個值之一:together 動畫同時執行,預設值sequentially 動畫按順序執行

那如果想有些動畫同時執行,有些按順序執行,該怎麼辦呢?因為set標籤是可以巢狀其他set標籤的,也就是說可以將同時執行的組合在一個set標籤,再嵌在按順序執行的set標籤內。

<!-- res/animator/animator_set.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">
    <objectAnimator
        android:duration="3000"
        android:propertyName="width"
        android:valueFrom="100"
        android:valueTo="20"
        android:valueType="intType" />
    <objectAnimator
        android:duration="3000"
        android:propertyName="marginTop"
        android:valueFrom="0"
        android:valueTo="100"
        android:valueType="intType" />
</set>

以上程式碼可實現兩個同時執行的動畫,一個將width從100縮放到20,一個將marginTop從0增加到100。多了一個marginTop屬性,那麼,在ViewWrapper新增setMarginTop()方法,新增後的ViewWrapper類程式碼如下:

private static class ViewWrapper {
    private View target;
    private int maxWidth;

    public ViewWrapper(View target, int maxWidth) {
        this.target = target;
        this.maxWidth = maxWidth;
    }

    public int getWidth() {
        return target.getLayoutParams().width;
    }

    public void setWidth(int widthValue) {
        target.getLayoutParams().width = maxWidth * widthValue / 100;
        target.requestLayout();
    }

    public void setMarginTop(int margin) {
        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) target.getLayoutParams();
        layoutParams.setMargins(0, margin, 0, 0);
        target.setLayoutParams(layoutParams);
    }
}

最後,動畫處理的程式碼:

public void onScaleWidth(View view) {
    // 獲取螢幕寬度
    int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
    // 將目標view進行包裝
    ViewWrapper wrapper = new ViewWrapper(view, maxWidth);
    // 將xml轉化為ObjectAnimator物件
    AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.animator_set);
    // 設定動畫的目標物件為包裝後的view
    animatorSet.setTarget(wrapper);
    // 啟動動畫
    animatorSet.start();
}

這樣就實現了寬度縮放和垂直移動的效果。

當然,在Android3.0之後,Google給View增加了animate方法來直接驅動屬性動畫,程式碼如下:

 view.animate().x(50f).y(100f);

小結

目前到此為止,我們瞭解到在Android中有三種不同的動畫型別,在Android3.0版本其實是一個區分點,在3.0之前用的居多都是View Animation,而在3.0之後用的居多是Property Animation,本次介紹的是這三者之間的區別和不同用法,下篇介紹具體例項和自定義動畫方面的知識點,敬請期待。

閱讀擴充套件

源於對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中可以看到技術積累的過程。
1,Android系統簡介
2,ProGuard程式碼混淆
3,講講Handler+Looper+MessageQueue關係
4,Android圖片載入庫理解
5,談談Android執行時許可權理解
6,EventBus初理解
7,Android 常見工具類
8,對於Fragment的一些理解
9,Android 四大元件之 " Activity "
10,Android 四大元件之" Service "
11,Android 四大元件之“ BroadcastReceiver "
12,Android 四大元件之" ContentProvider "
13,講講 Android 事件攔截機制
14,Android 動畫的理解
15,Android 生命週期和啟動模式
16,Android IPC 機制
17,View 的事件體系
18,View 的工作原理
19,理解 Window 和 WindowManager
20,Activity 啟動過程分析
21,Service 啟動過程分析
22,Android 效能優化
23,Android 訊息機制
24,Android Bitmap相關
25,Android 執行緒和執行緒池
26,Android 中的 Drawable 和動畫
27,RecylerView 中的裝飾者模式
28,Android 觸控事件機制
29,Android 事件機制應用
30,Cordova 框架的一些理解
31,有關 Android 外掛化思考
32,開發人員必備技能——單元測試

相關文章