Android動畫攻略—幀動畫、補間動畫、屬性動畫

前言

  • 動畫時Android開發中使用頻率比較高的功能。
  • 對Android提供的補間動畫,幀動畫以及屬性動畫做出歸納總結。

目錄

目錄.png

1. 幀動畫

幀動畫總體實現比較簡單,其實現本身是實現一個圖片集的連續播放,從而達到動畫的效果。

實現幀動畫就必須將大量圖片資源加入到APK當中,從而增加APK的大小,但是卻可以實現比較復雜的動畫效果。

幀動畫使用比較簡單的,具體操作過程如下

  1. 將圖片集導入到對應目錄下
  2. 在drawable文件夾下新建文件anim_chat. xml,的代碼實現如下
<?xml version="1.0" encoding="UTF-8"?>
<animation-list android:oneshot="false"
    xmlns:android="http://schemas.android.com/apk/res/android">
    //duraction字段可以用來設置該圖片播放時長,drawable用來設置要顯示的圖片
    <item android:duration="230" android:drawable="@drawable/ic_chat_recording1" />
    <item android:duration="230" android:drawable="@drawable/ic_chat_recording2" />
    <item android:duration="230" android:drawable="@drawable/ic_chat_recording3" />
    <item android:duration="230" android:drawable="@drawable/ic_chat_recording4" />
    <item android:duration="230" android:drawable="@drawable/ic_chat_recording5" />
    <item android:duration="230" android:drawable="@drawable/ic_chat_recording6" />
</animation-list>
  1. 在布局文件activity_main.xml當中添加組件
<Button
        android:id="@+id/frame_animation_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="幀動畫測試" />

    <ImageView
        android:id="@+id/frame_animation_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_chat_recording1"
        android:layout_marginTop="20dp"/>
  1. 在activity當中加入java代碼實現
public class MainActivity extends AppCompatActivity {

    private Button frameButton;
    private ImageView frameImage;
    private AnimationDrawable frameAnimation;
    boolean isStart = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFrame();
    }

    private void initFrame() {
        //初始化控件
        frameButton = (Button) findViewById(R.id.frame_animation_test);
        frameImage = (ImageView) findViewById(R.id.frame_animation_img);
        //給ImageView設置drawable
        frameImage.setImageResource(R.drawable.anim_chat_recording);
        //給動畫資源賦值
        frameAnimation = (AnimationDrawable) frameImage.getDrawable();
        //給按鈕添加點擊事件用來控制動畫
        frameButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //當isStart=false時表示動畫沒有在播放,點擊按鈕開始播放
                if (!isStart) {
                    frameAnimation.start();
                    isStart = true;
                } else {//當isStart=true時表示動畫正在播放,點擊按鈕停止播放
                    frameAnimation.stop();
                    isStart = false;
                }
            }
        });
    }
}

運行代碼進行測試


幀動畫--語音.gif

幀動畫也存在使用純java代碼的實現方式,但是在應用當中并不多見,有興趣可以了解一下,這里不做介紹。

2. 補間動畫

與按幀播放的幀動畫不同,補間動畫只需要定義初始和結束時的狀態,中間的動畫過程將由系統自動補齊。

  • 特點:

    • 補間動畫作用于View,可以實現視覺上的動畫效果,但是并沒有真正對視圖做出改變。
    • 使用簡單,可以使用非常簡單的方式實現動畫效果。
  • 實現方式可以有jave代碼實現和XML代碼實現兩種。

  • 分類:補間動畫可分為四類

jave xml 效果
AlphaAnimation alph 漸變透明度動畫效果
ScaleAnimation scale 漸變尺寸伸縮動畫效果
TranslateAnimation1 translate 畫面轉換位置移動動畫效果
RotateAnimation rotate 畫面轉移旋轉動畫效果

后文將對四種補間動畫效果做具體說明。

2.1 alph動畫

特有屬性:

  • android:fromAlpha:動畫開始時的透明度。
  • android:toAlpha:動畫結束時的透明度。

Java代碼實現

直接上代碼

  1. 在activity_main.xml當中定義布局資源
<ImageView
        android:id="@+id/alph_animation_img"
        android:layout_width="150dp"
        android:layout_height="200dp"
        android:src="@drawable/animation_test1"/>

后續內容的動畫效果基本針對圖片涉及到的xml布局都基本類似,將不再進行說明。

  1. 在activity當中進行實現
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initAlphJava();
    }

    private void initAlphJava() {
        alphImage = (ImageView) findViewById(R.id.alph_animation_img);
        alphImage.setVisibility(View.VISIBLE);
        //構造參數中,第一個參數為動畫開始時候透明度,第二個參數toAlpha為 動畫結束時候透明度
        //對于fromAlpha和toAlpha,1.0代表完全不透明狀態,0.0代表完全透明狀態
        AlphaAnimation mAlpha = new AlphaAnimation(1.0f,0.0f);
        //設置動畫播放時間,2000MS=2S
        mAlpha.setDuration(2000);
        //設置動畫循環次數,-1為一直循環
        mAlpha.setRepeatCount(-1);
        //設置動畫循環方式Animation.REVERSE為倒敘播放,Animation.RESTART為重復播放
        mAlpha.setRepeatMode(Animation.REVERSE);
        //alphImage開始播放動畫
        alphImage.startAnimation(mAlpha);
    }

動畫效果為:由原圖顯示漸變為隱藏狀態。


alph漸入漸出.gif

XML實現

  1. 在res目錄中新建anim文件夾。
  2. 在anim目錄中新建一個alph_anim.xml文件(注意文件名小寫)。
  3. 在alph_anim.xml文件當中對動畫進行定義。
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!--透明度控制動畫效果 alpha-->
    <!--fromAlpha 屬性為動畫起始時透明度 0.0表示完全透明 1.0表示完全不透明-->
    <!--toAlpha   屬性為動畫結束時透明度-->
    <!--duration  屬性為動畫持續時間-->
    <alpha
        android:duration="2000" //播放時間為2秒
        android:fromAlpha="1.0" //初始透明度為完全顯示
        android:toAlpha="0.0"   //結束透明度為完全透明
        android:repeatCount= "-1" />    //重復播放次數為無限循環
</set>

fromAlpha和toAlpha為alph動畫的特有屬性,1.0代表完全不透明狀態,0.0代表完全透明狀態

  1. 在activity當中對動畫資源信息引用。
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initAlphXml();
    }

    private void initAlphXml() {
        alphImage = (ImageView) findViewById(R.id.alph_animation_img);
        Animation animation = AnimationUtils.loadAnimation(this,R.anim.alph_anim);
        alphImage.startAnimation(animation);
    }

運行程序,動畫效果與上述java實現相同。
補間動畫中有一些公共屬性,其說明如下:

android:duration: 動畫執行的時間,以毫秒為單位

android:fillEnabled:true|false 動畫結束時還原到開始動畫前的狀態

android:fillBefore:true|false 動畫結束后視圖會停留在動畫開始的狀態,如果fillEnabled的值為true,它的值才有意義,否則沒有意義。默認值是true。

android:fillAfter:true|false 動畫結束后是否保留這個動畫的最后一幀的效果,它的設置不受fillEnabled的影響

android:repeatMode:reverse|restart 重復類型,reverse:表示倒序回放,restart:表示重新放一遍,這個屬性必須與repeatCount聯合使用,因為它的前提是重復,即重復播放時的播放類型。

android:repeatCount:動畫重復的次數(注意是重復的次數),設定具體數值,也是可以是infinite,表示無限循環

android:interpolator:設定的插值器,它主要用來為動畫設置一些特殊的效果,比方說:加速運動、減速運動等等。

2.2 scale動畫

特有屬性:

  • android:fromXScale起始的X方向上相對自身的縮放比例,類型float
  • android:toXScale:結尾的X方向上相對自身的縮放比例,類型float
  • android:fromYScale:起始的Y方向上相對自身的縮放比例,類型float
  • android:toYScale:結尾的Y方向上相對自身的縮放比例,類型float
  • android:pivotX: 縮放起點X軸坐標,可以是數值、百分數、百分數p。
  • android:pivotY: 縮放起點Y軸坐標,同pivotX。

java代碼實現

  1. 在activity_main.xml當中定義布局資源
  2. 在java代碼當中的代碼實現如下:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initScaleJava();
    }

    private void initScaleJava() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        ScaleAnimation scaleAnimation = new ScaleAnimation(1,2,1,2,Animation.RELATIVE_TO_SELF,
0.5f,Animation.RELATIVE_TO_SELF,0.5f);
        // fromX:動畫起始X軸縮放倍數
        // toX:動畫結束時X軸縮放倍數
        // fromY:動畫起始Y軸縮放倍數
        // toY:動畫結束時Y軸縮放倍數
        // pivotXType:X軸縮放原點參數類型
        // pivotXValue:X軸縮放原點參數
        // pivotYType:Y軸縮放原點參數類型
        // pivotYValue:Y軸縮放原點參數
        scaleAnimation.setDuration(3000);
        scaleAnimation.setRepeatMode(Animation.REVERSE);
        //Animation.INFINITE與-1取值相同,為無限重播
        scaleAnimation.setRepeatCount(Animation.INFINITE);
        mImage.startAnimation(scaleAnimation);
    }

動畫實現效果:以自身中心點為原點,縮放為原大小的兩倍。


scale動畫-圖片縮放.gif

構造方法

ScaleAnimation(float fromX, float toX, float fromY, float toY,
            int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

參數取值介紹

對于前四個參數:0.0表示收縮到沒有 1.0表示正常無伸縮 值小于1.0表示收縮 值大于1.0表示放大
pivotXType存在三種取值
pivotXType = Animation.ABSOLUTE:縮放軸點的x坐標 = View左上角的原點 在x方向 + pivotXValue數值的點(y方向同理)

pivotXType = Animation.RELATIVE_TO_SELF:縮放軸點的x坐標 = View左上角的原點 在x方向 + 自身寬度乘上pivotXValue數值的值(y方向同理)

pivotXType = Animation.RELATIVE_TO_PARENT:縮放軸點的x坐標 = View左上角的原點 在x方向 + 父控件寬度乘上pivotXValue數值的值 (y方向同理)

例子中所選參數為從原圖大小X軸和Y軸縮放到原大小的兩倍,縮放參照點為以自身寬高比例的50%處,也就是中心點。

XML實現

  1. 在res目錄中新建anim文件夾。
  2. 在anim目錄中新建一個scale_anim.xml文件(注意文件名小寫)。
  3. 在scale_anim.xml文件當中對動畫進行定義。
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <scale
        android:duration="3000"  //設置播放時長為3秒
        android:fillAfter="false"   //設置不保存播放完畢之后的畫面
        android:fromXScale="1.0"    //起始畫面X軸縮放倍數
        android:fromYScale="1.0"    //起始畫面Y軸縮放倍數
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
        //使用accelerate_decelerate_interpolator加速--減速差值器
        android:pivotX="50%"    //X軸縮放原點為自身寬度的50%
        android:pivotY="50%"    //Y軸縮放原點為自身寬度的50%
        android:toXScale="2.0"  //結束畫面X軸縮放倍數
        android:toYScale="2.0"  //結束畫面Y軸縮放倍數
        android:repeatCount= "-1"   //動畫循環次數為無限循環
        android:repeatMode="reverse"/>  //循環模式為倒播

</set>

pivotX與pivotY相同,有三種取值方式:

  • 取值為數值:當為數值時,表示在當前View的左上角,加上參數值即原點處,做為旋轉點X坐標,單位為px。
  • 取值為百分數:如果是50%表示在當前控件的左上角加上自己寬度的50%做為旋轉點X坐標。
  • 取值為百分數p:如果是50%p,那么就是表示在當前的左上角加上父控件寬度的50%做為旋轉點X坐標。
  1. 在activity當中對動畫資源信息引用。
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initScaleXML();
    }

    private void initAlphXml() {
        private void initScaleXML() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        Animation animation = AnimationUtils.loadAnimation(this,R.anim.scale_anim);
        mImage.startAnimation(animation);
    }
    }

運行程序,動畫效果與上述java實現相同。

2.3 translate動畫

特有屬性:

  • android:fromXDelta:起始點X軸坐標。
  • android:fromYDelta:起始點Y坐標。
  • android:toXDelta:結束點X坐標
  • android:toYDelta:結束點Y坐標

java代碼實現

  1. 在activity_main.xml當中定義布局資源
  2. 在java代碼當中的代碼實現如下:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initTranslateJava();
    }
    
    private void initTranslateJava() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        TranslateAnimation translateAnimation = new TranslateAnimation(0,300,0,300);
        //fromXDelta:起始點X軸的坐標
        //toXDelta:終止點X軸的坐標
        //fromYDelta:起始點Y軸的坐標
        //toYDelta:終止點Y軸的坐標
        translateAnimation.setDuration(2000);
        translateAnimation.setRepeatMode(Animation.REVERSE);
        //Animation.INFINITE與-1取值相同,為無限重播
        translateAnimation.setRepeatCount(Animation.INFINITE);
        mImage.startAnimation(translateAnimation);
    }

上述代碼實現效果:View向右下角45°移動,最終坐標為原左上角坐標的X軸正方向300,Y軸正方向300.
動畫效果如下


translate動畫-圖片移動.gif

對于構造方法

public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
            int fromYType, float fromYValue, int toYType, float toYValue) {

參數類型可大體分為兩種: value和type

Type參數取值介紹

  • fromXType = Animation.ABSOLUTE:縮放軸點的x坐標 = View左上角的原點 在x方向 加上 pivotXValue數值的點(y方向同理) 默認為這種方式。

  • fromXType = Animation.RELATIVE_TO_SELF:縮放軸點的x坐標 = View左上角的原點 在x方向 加上 自身寬度乘上pivotXValue數值的值(y方向同理)

  • fromXType = Animation.RELATIVE_TO_PARENT:縮放軸點的x坐標 = View左上角的原點 在x方向 加上 父控件寬度乘上pivotXValue數值的值 (y方向同理)

Value取值介紹:

有三種取值方式:

  • 取值為數值:當為數值時,表示在當前View的左上角,即原點處加上參數值,做為旋轉點X坐標,單位為px。
  • 取值為百分數:如果是50%表示在當前控件的左上角加上自己寬度的50%做為原點X坐標。
  • 取值為百分數p:如果是50%p,那么就是表示在當前的左上角加上父控件寬度的50%做為原點X坐標。

XML實現

  1. 在res目錄中新建anim文件夾。
  2. 在anim目錄中新建一個translate_anim.xml文件(注意文件名小寫)。
  3. 在translate_anim.xml文件當中對動畫進行定義。
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="2000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="300"
        android:toYDelta="300" />

</set>

參數值類型介紹

整型值:

fromXDelta 屬性為動畫起始時 X坐標上的位置

toXDelta 屬性為動畫結束時 X坐標上的位置

fromYDelta 屬性為動畫起始時 Y坐標上的位置

toYDelta 屬性為動畫結束時 Y坐標上的位置

注意:

沒有指定fromXType toXType fromYType toYType 時候, 默認是以自己為相對參照物

長整型值:

duration 屬性為動畫持續時間

說明: 時間以毫秒為單位

  1. 在activity當中對動畫資源信息引用。
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initTranslateXML();
    }

    private void initTranslateXML() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        Animation animation = AnimationUtils.loadAnimation(this,R.anim.translate_anim);
        mImage.startAnimation(animation);
    }

運行程序,動畫效果與上述java實現相同。

2.4 Rotate動畫

特有屬性:

  • android:fromDegrees:動畫開始時旋轉的角度位置,float類型,正值代表順時針方向度數,負值代碼逆時針方向度數
  • android:toDegrees: 動畫結束時旋轉到的角度位置,float類型,正值代表順時針方向度數,負值代碼逆時針方向度數
  • android:pivotX:旋轉點X軸坐標,float類型,可以是數值、百分數、百分數p三種樣式。
  • android:pivotY:旋轉點Y軸坐標,取值及意義跟android:pivotX一樣。

Java實現

  1. 在activity_main.xml當中定義布局資源
  2. 在java代碼當中的代碼實現如下:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initRotateJava();
    }
    private void initRotateJava() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        RotateAnimation rotateAnimation = new RotateAnimation(0,90,Animation.RELATIVE_TO_SELF,
0.5f,Animation.RELATIVE_TO_SELF,0.5f);
//        參數說明:
//        fromDegrees :動畫開始時 視圖的旋轉角度(正數 = 順時針,負數 = 逆時針)
//        toDegrees :動畫結束時 視圖的旋轉角度(正數 = 順時針,負數 = 逆時針)
//        pivotXType:旋轉軸點的x坐標的模式
//        pivotXValue:旋轉軸點x坐標的相對值
//        pivotYType:旋轉軸點的y坐標的模式
//        pivotYValue:旋轉軸點y坐標的相對值
        rotateAnimation.setRepeatMode(Animation.REVERSE);
//        視頻循環模式
        rotateAnimation.setDuration(2000);
//        視頻播放時長
        rotateAnimation.setRepeatCount(Animation.INFINITE);
//        視頻循環次數
        mImage.startAnimation(rotateAnimation);

上述代碼實現效果:以自身中心點為坐標,順時針旋轉90度。


rotate動畫-圖片旋轉.gif

參數取值介紹

fromDegrees :動畫開始時 視圖的旋轉角度(正數 = 順時針,負數 = 逆時針)

toDegrees :動畫結束時 視圖的旋轉角度(正數 = 順時針,負數 = 逆時針)

XML實現

  1. 在res目錄中新建anim文件夾。
  2. 在anim目錄中新建一個set_anim.xml文件(注意文件名小寫)。
  3. 在set_anim.xml文件當中對動畫進行定義。
<set xmlns:android="http://schemas.android.com/apk/res/android">
 <rotate
        android:duration="3000"
        android:fromDegrees="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="90" />    
</set>

<!--
     rotate 旋轉動畫效果
       屬性:interpolator 指定一個動畫的插入器
             在我試驗過程中,使用android.res.anim中的資源時候發現
             有三種動畫插入器:
                accelerate_decelerate_interpolator   加速-減速 動畫插入器
                accelerate_interpolator               加速-動畫插入器
                decelerate_interpolator               減速- 動畫插入器
             其他的屬于特定的動畫效果
                           
       浮點數型值:
            fromDegrees 屬性為動畫起始時物件的角度    
            toDegrees   屬性為動畫結束時物件旋轉的角度 可以大于360度   

        
            說明:
                     當角度為負數——表示逆時針旋轉
                     當角度為正數——表示順時針旋轉              
                     (負數from——to正數:順時針旋轉)   
                     (負數from——to負數:逆時針旋轉) 
                     (正數from——to正數:順時針旋轉) 
                     (正數from——to負數:逆時針旋轉)       

            pivotX     屬性為動畫相對于物件的X坐標的開始位置
            pivotY     屬性為動畫相對于物件的Y坐標的開始位置
                
            說明:        以上兩個屬性值 從0%-100%中取值
                         50%為物件的X或Y方向坐標上的中點位置

        長整型值:
            duration  屬性為動畫持續時間
            說明:       時間以毫秒為單位

    -->
  1. 在activity當中對動畫資源信息引用。
   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initRotateXML();
    }

   private void initRotateXML() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.rotate_anim);
        mImage.startAnimation(animation);
    }

運行程序,動畫效果與上述java實現相同。

2.5 組合動畫(set標簽)

使用set標簽可以做到多種動畫類型的組合效果具體步驟如下

  1. 在res目錄中新建anim文件夾。
  2. 在anim目錄中新建一個set_anim.xml文件(注意文件名小寫)。
  3. 在set_anim.xml文件當中對動畫進行定義。
<set xmlns:android="http://schemas.android.com/apk/res/android">
 <alpha  
        android:fromAlpha= "0.0"  
        android:toAlpha= "1.0"  
        android:duration= "3000" />
        //alph動畫 實現效果:從透明狀態漸變到顯示狀態
     
    <scale  
        android:fromXScale= "0.0"  
        android:toXScale= "1.0"  
        android:fromYScale= "0.0"  
        android:toYScale= "1.0"  
        android:pivotX= "50%"  
        android:pivotY= "50%"  
        android:duration= "3000" />  
        //以自身原點為坐標從0縮放原圖大小
     
    <rotate  
        android:fromDegrees= "0"  
        android:toDegrees= "720"  
        android:pivotX= "50%"  
        android:pivotY= "50%"  
        android:duration= "3000"/>  
        //旋轉720°
     
     <translate  
        android:startOffset= "3000"  
        android:fromXDelta= "0"  
        android:fromYDelta= "0"  
        android:toXDelta= "85"  
        android:toYDelta= "0"  
        android:duration= "1000" />  
        //3000毫秒以后,水平方向移動85距離
      
    <alpha  
        android:startOffset= "4000"  
        android:fromAlpha= "1.0"  
        android:toAlpha= "0.0"  
        android:duration= "1000" />  
        //4000毫秒以后圖片漸變消失
</set>
  1. 在==activity==當中對動畫資源信息引用。
   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       initSet();
    }

  private void initSet() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.set_anim);
        animation.setRepeatCount(-1);
        mImage.startAnimation(animation);
    }

動畫效果如下:


set集合動畫.gif

補間動畫使用方便,但是其針對view進行操作的特性,以及只能實現視圖上的動畫而不能改變實際的view位置大小等缺點使其功能受到限制。下面將介紹功能更加強大的屬性動畫。

3.屬性動畫

  • 優勢:
    • 在Android3.0推出了屬性動畫。相比于屬性動畫,View動畫一個非常大的缺陷就是其不具備交互性。當某個元素發生View動畫之后,其相應事件的位置依然在進行前的地方,所以View動畫只能做到普通的動畫效果,盡量避免交互操作。View動畫的優勢也十分明顯:效率比較高,使用也方便。
    • View動畫只能實現比較單一的View的移動縮放等效果,效果比較單一,屬性動畫則是通過屬性值變化來重新布局View,理論上可以實現任何動畫效果。
  • 問題:
    • 在使用上不如View動畫方便。

3.1 原理解析

屬性動畫有兩個非常重要的類:ValueAnimator 類 和 ObjectAnimator 類

  • ValueAnimator:通過不斷控制值的變化,再不斷手動賦給對象的屬性,從而實現動畫效果。
  • ObjectAnimator:直接對對象的屬性值進行改變操作,通過不斷控制值的變化,再不斷自動賦給對象的屬性,從而實現動畫效果。其實現繼承了ValueAnimator。

3.2 差值器和估值器

  • 插值器(Interpolator)前面我們在介紹補間動畫時就有提到過,它主要用來為動畫設置一些特殊的效果,比方說:加速運動、減速運動、動畫結束的時候彈等等。主要有
    • accelerate_decelerate_interpolator 加速-減速 動畫插入器
    • accelerate_interpolator 加速-動畫插入器
    • decelerate_interpolator 減速- 動畫插入器
  • 估值器(TypeEvaluator)決定值的具體變化數值

我們先來看一下系統提供的估值器:

public class FloatEvaluator implements TypeEvaluator<Number> {
    /**
     * @param fraction   The fraction from the starting to the ending values
     * @param startValue The start value; should be of type <code>float</code> or
     *                   <code>Float</code>
     * @param endValue   The end value; should be of type <code>float</code> or <code>Float</code>
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public Float evaluate(float fraction, Number startValue, Number endValue) {
    <!--fraction 動畫的完成度-->
    <!--startValue 動畫的初始值-->
    <!--endValue 動畫的結束值-->
        float startFloat = startValue.floatValue();
        <!--計算返回結果的公式為result = x0 + t * (v1 - v0)-->
        <!--x0=startValue-->
        <!--x1 = endValue-->
        <!--t = fraction-->
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}

這是由系統提供的操作float類型數據的估值器,可以根據我們提供的fraction、startValue以及endValue不斷地產float數值對象返回給我們,我們可以根據返回的數據對我們的View進行更改。

但是很多情況下我們需要操作的不只是float或者int類型等基本數據類型,所以我們就需要自定義==ObjectEvaluator==。

模仿==FloatEvaluator==,自定義==ObjectEvaluator==如下:

// 實現TypeEvaluator接口
public class ObjectEvaluator implements TypeEvaluator<Object>{  

// 復寫evaluate()
// 在evaluate()里寫入對象動畫過渡的邏輯
    @Override  
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
      
        ... // 寫入對象動畫過渡的邏輯
        // 返回對象動畫過渡的邏輯計算后的值
        return value;  
    }

后面會進行實例說明。再回過頭來看我們的主角:ValueAnimator和ObjectAnimator類

3.3 ObjectAnimator類

本質原理: 通過不斷控制值的變化,再不斷自動賦給對象的屬性,從而實現動畫效果。
先來看一個簡單的例子:

 private void initObjectAnimationJava() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        ObjectAnimator alpha = ObjectAnimator.ofFloat(mImage, "alpha", 0f, 1f);
        alpha.setDuration(2000);//設置動畫時間
        alpha.setInterpolator(new DecelerateInterpolator());//設置動畫插入器,減速
        alpha.setRepeatCount(-1);//設置動畫重復次數,這里-1代表無限
        alpha.setRepeatMode(ValueAnimator.REVERSE);//設置動畫循環模式,注意這里是ValueAnimator.REVERSE而不是Animator.REVERSE
        alpha.start();//啟動動畫。
    }

實現效果:圖片由隱藏漸漸顯示。

動畫對象定義完后的操作與View動畫基本一致,最主要的區別在于構造方法,這里我使用了ObjectAnimator.ofFloat,ofFloat()方法主要有兩個作用:設置參數以及返回ObjectAnimator對象,其方法如下:

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setFloatValues(values);
        return anim;
    }
    //1. 需要操作的對象,注意這里與補間動畫不同,不止限于View對象,而可以是任意操作對象。
    //2. propertyName需要操作的屬性,這個屬性必須要有get和set方法才可以。
    //3. 這是一個可變參數,為后續的變化趨勢。如設置兩個參數v1,v2,則屬性由v1變化到v2,如果設置的是v1,v2,v3,則由v1變化到v2再變化到v3以此類推。
  • 透明度:
ObjectAnimator alpha = ObjectAnimator.ofFloat(text, "alpha", 0f, 1f);

透明度由0.0變為1.0。

  • 伸縮X軸:
ObjectAnimator scaleX = ObjectAnimator.ofFloat(text, "scaleX", 0f, 1f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(text, "scaleY", 0f, 1f);

X軸(Y軸)方向由0縮放到原圖大小。

  • 移動:
ObjectAnimator translationUp = ObjectAnimator.ofFloat(text, "Y",0f,300f, 0f);

Y軸方向:0f-> -300f,向左;-300f-> 0f,向右。

  • 旋轉
ObjectAnimator animator = ObjectAnimator.ofFloat(text, "rotation", 0f, 360f, 0f);

旋轉動畫:0f -> 360f ,順時針; 360f -> 0f,逆時針。
可以操作的動畫屬性propertyName的取值可以為:

屬性 作用 數值類型
Alpha 控制View的透明度 float
TranslationX 控制X方向的位移 float
TranslationY 控制Y方向的位移 float
ScaleX 控制X方向的縮放倍數 float
ScaleY 控制Y方向的縮放倍數 float
Rotation 控制以屏幕方向為軸的旋轉度數 float
RotationX 控制以X軸為軸的旋轉度數 float
RotationY 控制以Y軸為軸的旋轉度數 float

ObjectAnimation組合動畫

Java代碼

使用AnimatorSet完成組合動畫播放。

  • anim1.with(anim2):anim1與anim2同時播放。
  • anim1.befor(anim2):在anim2之前播放anim1。
  • anim1.after(anim2):在anim2之后播放anim1。
  • anim1.after(long delay):delay時間之后播放anim1。

具體使用代碼如下:

private void initObjectAnimationSetJava() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        AnimatorSet set = new AnimatorSet() ;
        //X軸旋轉180°
        ObjectAnimator rotationX = ObjectAnimator .ofFloat(mImage, "rotationX", 0f, 180f);
        rotationX.setDuration(2000);
        //Y軸旋轉180°
        ObjectAnimator rotationY = ObjectAnimator .ofFloat(mImage, "rotationY", 0f, 180f);
        rotationY.setDuration(2000);
        //X軸方向縮放到原圖大小
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(mImage, "scaleX", 0f, 1f);
        scaleX.setDuration(2000);
        //Y軸方向縮放到原圖大小
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(mImage, "scaleY", 1f, 0f);
        scaleY.setDuration(2000);

        set.play(rotationX).with(scaleX); //X軸旋轉與X軸縮放動畫同時進行
        set.play(rotationX).before(rotationY); //X軸旋轉動畫完成以后開始Y軸旋轉
        set.play(rotationY).with(scaleY);//Y軸旋轉動畫與Y軸縮放同時開始
        set.start(); //開始播放動畫
    }

動畫效果:X軸旋轉與X軸縮放動畫同時進行,結束之后Y軸旋轉動畫與Y軸縮放同時開始。實際效果如下:


ObjectAnimation組合動畫.gif

XML實現

set中的屬==性android:ordering==:規定了這個set中的動畫的執行順序,包括:

  • together(默認):set中的動畫同時執行
  • sequentially:set中的動畫按順序執行
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">
        <set android:ordering="together">
            <objectAnimator
                android:duration="2000"
                android:propertyName="rotationX"
                android:repeatMode="reverse"
                android:valueFrom="0"
                android:valueTo="180" />
            <objectAnimator
                android:duration="2000"
                android:propertyName="scaleX"
                android:repeatMode="reverse"
                android:valueFrom="0.0"
                android:valueTo="1.0" />
        </set>

        <set android:ordering="together">
            <objectAnimator
                android:duration="2000"
                android:propertyName="rotationY"
                android:repeatMode="reverse"
                android:valueFrom="0"
                android:valueTo="180" />
            <objectAnimator
                android:duration="2000"
                android:propertyName="scaleY"
                android:repeatMode="reverse"
                android:valueFrom="1.0"
                android:valueTo="0.0" />
        </set>
</set>

java代碼:

private void initObjectAnimationSetXML() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        Animator animator = AnimatorInflater.loadAnimator(this,R.animator.object_set_anim);
        animator.setTarget(mImage);
        animator.start();
    }

實現效果與java代碼一致。

3.4 ValueAnimator類

ValueAnimator類本質上是一種值得操作機制,想要實現動畫,是需要開發者手動將這些值 賦給對象的屬性值。
ValueAnimator動畫同樣存在java和xml兩種使用方式。建議使用java代碼的方式,這里只介紹java方式,xml有興趣的可以自己了解一下。

ValueAnimator類常用的方法與ObjectAnimation類似,主要方法有

  • public static ValueAnimator ofInt(int... values)
  • public static ValueAnimator ofArgb(int... values)
  • public static ValueAnimator ofFloat(float... values)
  • public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)

前三種方法使用類似,后面的參數都表示變化趨勢,即:設置兩個參數v1,v2,則屬性由v1變化到v2,如果設置的是v1,v2,v3,則由v1變化到v2再變化到v3以此類推。

以ofFloat為例,使用方法如下

private void initValueAnimationJava() {
        mImage = (ImageView) findViewById(R.id.animation_img);
        //步驟1. 定義ValueAnimator方法
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f,1.0f);
        //獲取原控件寬高
        final int width = mImage.getLayoutParams().width;
        final int height = mImage.getLayoutParams().height;
        //設置動畫屬性
        valueAnimator.setDuration(2000);
        valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        //步驟2. 添加AnimatorUpdateListener方法永聯監聽值得變化趨勢
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animator) {

                float currentValue = (Float) animator.getAnimatedValue();
                // 獲得每次變化后的屬性值

                // 步驟3:每次值變化時,將值手動賦值給對象的屬性
                // 即將每次變化后的值 賦 給按鈕的寬度,這樣就實現了按鈕寬度屬性的動態變化

                mImage.getLayoutParams().width = (int) (width*currentValue);
                mImage.getLayoutParams().height = (int) (height*currentValue);
                

                // 步驟4:刷新視圖,即重新繪制,從而實現動畫效果
                mImage.requestLayout();

            }
        });
        valueAnimator.start();
    }

實際動畫效果如下:


valueAnimation-圖片縮放.gif

如果我們想實現一些比較負載的動畫效果,而系統提供的估值器滿足不來我們時怎么辦,這時候我們之前提到的估值器TypeEvaluator就該登場了,通過方法public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values),設置估值器以后將對數據按照估值器進行計算并返回值,通過點贊動畫例子了解實戰應用。

4. 屬性動畫實戰--點贊動畫

不多說,直接上代碼

  1. 首先是點贊動畫的自定義==PraiseLayout==
public class PraiseLayout extends RelativeLayout {
    //底部點贊按鈕寬高
    private int dHeight;
    private int dWidth;
    //整個View空間寬高
    private int mHeight;
    private int mWidth;
    private LayoutParams layoutParams;
    private Random random = new Random();
    //圖片資源數組
    private Drawable[] drawables;

    public PraiseLayout(Context context) {
        super(context);
        init();
    }

    public PraiseLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PraiseLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        //初始化圖片資源
        drawables = new Drawable[4];
        Drawable red = ContextCompat.getDrawable(getContext(), R.drawable.nemo_heart_red);
        Drawable blue = ContextCompat.getDrawable(getContext(), R.drawable.nemo_heart_blue);
        Drawable orange = ContextCompat.getDrawable(getContext(), R.drawable.nemo_heart_orange);
        Drawable pink = ContextCompat.getDrawable(getContext(), R.drawable.nemo_heart_pink);

        drawables[0] = red;
        drawables[1] = blue;
        drawables[2] = orange;
        drawables[3] = pink;

        dHeight = red.getIntrinsicHeight();
        dWidth = red.getIntrinsicWidth();

        layoutParams = new LayoutParams(dWidth, dHeight);

        //點贊動畫開始于布局底部
        layoutParams.addRule(CENTER_HORIZONTAL, TRUE);
        layoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE);
    }

    //獲取組件大小
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mHeight = getMeasuredHeight();
        mWidth = getMeasuredWidth();
    }
    //點擊動畫事件
    public void addFavorWithoutBiz() {
        ImageView imageView = new ImageView(getContext());
        imageView.setImageDrawable(drawables[random.nextInt(4)]);
        imageView.setLayoutParams(layoutParams);

        addView(imageView);
        if (mWidth > 0 && dHeight > 0) {
            Animator set = getBezierValueAnimator(imageView);
            set.addListener(new AnimEndListener(imageView));
            set.start();
        }

    }
    //添加點贊動畫
    private ValueAnimator getBezierValueAnimator(View target) {
        //點贊動畫的估值器,參數為貝塞爾曲線兩個中間點
        BezierEvaluator evaluator = new BezierEvaluator(getPointF(2), getPointF(1));

        //參數1:估值器對象  參數2、3:三重貝塞爾曲線的起點和終止點。
        ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF((mWidth - dWidth) / 2, mHeight - dHeight - 20), new PointF(random.nextInt(getWidth()), 0));
        animator.addUpdateListener(new BezierListenr(target));
        animator.setTarget(target);
        animator.setDuration(3000);
        return animator;
    }
    private PointF getPointF(int scale) {

        PointF pointF = new PointF();

        pointF.x = random.nextInt((mWidth - 50));//減去50 是為了控制 x軸活動范圍,看效果 隨意~~
        //再Y軸上 為了確保第二個點 在第一個點之上,我把Y分成了上下兩半 這樣動畫效果好一些  也可以用其他方法
        pointF.y = random.nextInt((mHeight - 150)) / scale;
        return pointF;
    }


    //終止動畫監聽
    class AnimEndListener extends AnimatorListenerAdapter {
        private View target;
        public AnimEndListener(View target) {
            this.target = target;
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            removeView((target));
        }
    }

    //屬性動畫的監聽類,當數值更新時觸發
    class BezierListenr implements ValueAnimator.AnimatorUpdateListener {
        private View target;

        public BezierListenr(View target) {
            this.target = target;
        }

        @Override

        public void onAnimationUpdate(ValueAnimator animation) {
            //這里獲取到貝塞爾曲線計算出來的的x y值 賦值給view 這樣就能讓愛心隨著曲線走啦
            PointF pointF = (PointF) animation.getAnimatedValue();
            target.setX(pointF.x);
            target.setY(pointF.y);
            // alpha動畫
            target.setAlpha(1 - animation.getAnimatedFraction());
        }
    }

}
  1. 估值器==BezierEvaluator==的實現,這里使用三重貝塞爾曲線的公式計算。
public class BezierEvaluator implements TypeEvaluator<PointF> {

    private PointF pointF1;
    private PointF pointF2;

    public BezierEvaluator(PointF pointF1, PointF pointF2) {
        this.pointF1 = pointF1;
        this.pointF2 = pointF2;
    }

    @Override
    public PointF evaluate(float time, PointF startValue, PointF endValue) {
        float timeLeft = 1.0f - time;
        PointF point = new PointF();//結果

        point.x = timeLeft * timeLeft * timeLeft * (startValue.x)
                + 3 * timeLeft * timeLeft * time * (pointF1.x)
                + 3 * timeLeft * time * time * (pointF2.x)
                + time * time * time * (endValue.x);

        point.y = timeLeft * timeLeft * timeLeft * (startValue.y)
                + 3 * timeLeft * timeLeft * time * (pointF1.y)
                + 3 * timeLeft * time * time * (pointF2.y)
                + time * time * time * (endValue.y);

        return point;
    }
}
  1. 在xml布局文件中引入自定義布局
 <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.example.liubohua.myapplication.PraiseLayout
            android:id="@+id/animation_praise"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </com.example.liubohua.myapplication.PraiseLayout>

        <ImageView
            android:id="@+id/animation_praise_img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/nemo_heart_blue"
            android:layout_gravity="center|bottom"/>

    </FrameLayout>

  1. 在java代碼實現
 private void initPraise() {
        animation_praise_img = (ImageView) findViewById(R.id.animation_praise_img);
        animation_praise = (PraiseLayout) findViewById(R.id.animation_praise);
        animation_praise_img.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animation_praise.addFavorWithoutBiz();
            }
        });
    }

實現效果圖:


點贊動畫.gif

尾聲

差不多Android常見的動畫操作也就這些,通過簡單的學習再加上龐大的腦洞就可以實現非常炫酷的效果,趕緊嘗試一下吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374