鍍金池/ 教程/ Android/ 聯(lián)合動畫的 XML 實現(xiàn)與使用示例
聯(lián)合動畫的 XML 實現(xiàn)與使用示例
Interpolator 插值器
高級進階(二)
ObjectAnimator 基本使用
ValueAnimator 基本使用
alpha、scale、translate、rotate、set 的 xml 屬性及用法
PropertyValuesHolder 與 Keyframe
layoutAnimation 與 gridLayoutAnimation
自定義控件三部曲之動畫篇(十三)——實現(xiàn)ListView Item進入動畫
自定義控件三部曲之動畫篇(十二)——animateLayoutChanges與LayoutTransition
高級進階(一)
代碼生成 alpha、scale、translate、rotate、set 及插值器動畫
聯(lián)合動畫的代碼實現(xiàn)

聯(lián)合動畫的 XML 實現(xiàn)與使用示例

上篇給大家講了有關(guān) AnimatorSet 的代碼實現(xiàn)方法,這篇我們就分別來看看如何利用 xml 來實現(xiàn) ValueAnimator、ObjectAnimator 和 AnimatorSet; 在文章最后,將利用 AnimatorSet 來實現(xiàn)一個路徑動畫,效果圖如下:

http://wiki.jikexueyuan.com/project/android-animation/images/106.gif" alt="" />

(這里實現(xiàn)的是一個動畫菜單,在點擊菜單按鈕時,彈出各個菜單)

一、聯(lián)合動畫的 XML 實現(xiàn)

在 xml 中對應(yīng) animator 總共有三個標(biāo)簽,分別是

<animator />:對應(yīng) ValueAnimator
<objectAnimator />:對應(yīng) ObjectAnimator
<set />:對應(yīng) AnimatorSet

下面我們逐個來看各個標(biāo)簽的用法

1、animator

(1)、animator 所有字段及意義

下面是完整的 animator 所有的字段及取值范圍:

<animator
    android:duration="int"
    android:valueFrom="float | int | color"
    android:valueTo="float | int | color"
    android:startOffset="int"
    android:repeatCount="int"
    android:repeatMode=["repeat" | "reverse"]
    android:valueType=["intType" | "floatType"]
    android:interpolator=["@android:interpolator/XXX"]/>
  • android:duration:每次動畫播放的時長
  • android:valueFrom:初始動化值;取值范圍為 float,int 和 color,如果取值為 float 對應(yīng)的值樣式應(yīng)該為 89.0,取值為 Int 時,對應(yīng)的值樣式為:89;當(dāng)取值為 clolor 時,對應(yīng)的值樣式為 #333333;
  • android:valueTo:動畫結(jié)束值;取值范圍同樣是 float,int 和 color 這三種類型的值;
  • android:startOffset:動畫激活延時;對應(yīng)代碼中的 startDelay(long delay)函數(shù);
  • android:repeatCount:動畫重復(fù)次數(shù)
  • android:repeatMode:動畫重復(fù)模式,取值為 repeat 和 reverse;repeat 表示正序重播,reverse 表示倒序重播
  • android:valueType:表示參數(shù)值類型,取值為 intType 和 floatType;與 android:valueFrom、android:valueTo 相對應(yīng)。如果這里的取值為 intType,那么 android:valueFrom、android:valueTo 的值也就要對應(yīng)的是 int 類型的數(shù)值。如果這里的數(shù)值是 floatType,那么 android:valueFrom、android:valueTo 的值也要對應(yīng)的設(shè)置為 float 類型的值。非常注意的是,如果 android:valueFrom、android:valueTo 的值設(shè)置為 color 類型的值,那么不需要設(shè)置這個參數(shù);
  • android:interpolator:設(shè)置加速器;有關(guān)系統(tǒng)加速器所對應(yīng)的 xml 值對照表如下:

http://wiki.jikexueyuan.com/project/android-animation/images/19.png" alt="" />

(2)、將 xml 加載到程序中

在定義了一個 xml 后,我們需要將其加載到程序中,使用的方法如下:

ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(MyActivity.this,R.animator.animator);
valueAnimator.start();

通過 loadAnimator 將 animator 動畫的 xml 文件,加載進來,根據(jù)類型進行強轉(zhuǎn)。

(3)、簡單示例

下面我們就舉個例子來看看如何來使用 xml 生成對應(yīng)的 animator 動畫 先看看整體效果圖:

http://wiki.jikexueyuan.com/project/android-animation/images/107.gif" alt="" />

在效果圖中可以看到,我們生成了一個動畫,動態(tài)了改變了當(dāng)前控件的坐標(biāo)位置。 我們先在 res/animator 文件夾下生成一個動畫的 xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
          android:valueFrom="0"
          android:valueTo="300"
          android:duration="1000"
          android:valueType="intType"
          android:interpolator="@android:anim/bounce_interpolator"/>

在這里,我們將 valueType 設(shè)置為 intType,所以對應(yīng)的 android:valueFrom、android:valueTo 都必須是 int 類型的值;插值器使用 bounce 回彈插值器 然后看看加載到程序中過程:

ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(MyActivity.this,
       R.animator.animator);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {
       int offset = (int)animation.getAnimatedValue();
       mTv1.layout( offset,offset,mTv1.getWidth()+offset,mTv1.getHeight() + offset);
   }
});
valueAnimator.start();

由于我們 xml 中根屬性是所以它對應(yīng)的是 ValueAnimator,所以在加載后,將其強轉(zhuǎn)為 valueAnimator;然后對其添加控件監(jiān)聽。在監(jiān)聽時,動態(tài)改變當(dāng)前 textview 的位置。有關(guān)這些代碼就不再細講了,如果看到前面的文章,這段代碼應(yīng)該是無比熟悉的。 最后的效果就是開頭時所演示的效果。 源碼在文章底部給出 有關(guān) android:valueFrom、android:valueTo 取值為 color 屬性時的用法,這里就不講了,因為在 xml 中使用 color 屬性,我也不會用;嘗試了下,不成功,也不想嘗試了,沒什么太大意義,下面我們會講如何在 objectanimator 中使用 color 屬性;

2、objectAnimator

(1)字段意義及使用方法

同樣,我們先來看看它的所有標(biāo)簽的意義:

<objectAnimator
    android:propertyName="string"
    android:duration="int"
    android:valueFrom="float | int | color"
    android:valueTo="float | int | color"
    android:startOffset="int"
    android:repeatCount="int"
    android:repeatMode=["repeat" | "reverse"]
    android:valueType=["intType" | "floatType"]
    android:interpolator=["@android:interpolator/XXX"]/>

意義:

  • android:propertyName:對應(yīng)屬性名,即 ObjectAnimator 所需要操作的屬性名。 其它字段的意義與 animator 的意義與取值是一樣的,下面再重新列舉一下。
  • android:duration:每次動畫播放的時長
  • android:valueFrom:初始動化值;取值范圍為 float,int 和 color;
  • android:valueTo:動畫結(jié)束值;取值范圍同樣是 float,int 和 color 這三種類型的值;
  • android:startOffset:動畫激活延時;對應(yīng)代碼中的 startDelay(long delay)函數(shù);
  • android:repeatCount:動畫重復(fù)次數(shù)
  • android:repeatMode:動畫重復(fù)模式,取值為 repeat 和 reverse;repeat 表示正序重播,reverse 表示倒序重播
  • android:valueType:表示參數(shù)值類型,取值為 intType 和 floatType;與 android:valueFrom、android:valueTo 相對應(yīng)。如果這里的取值為 intType,那么 android:valueFrom、android:valueTo 的值也就要對應(yīng)的是 int 類型的數(shù)值。如果這里的數(shù)值是 floatType,那么 android:valueFrom、android:valueTo 的值也要對應(yīng)的設(shè)置為 float 類型的值。非常注意的是,如果 android:valueFrom、android:valueTo 的值設(shè)置為 color 類型的值,那么不需要設(shè)置這個參數(shù);
  • android:interpolator:設(shè)置加速器;

下面我們就看看如何使用:

ObjectAnimator animator = (ObjectAnimator) AnimatorInflater.loadAnimator(MyActivity.this,
        R.animator.object_animator);
animator.setTarget(mTv1);
animator.start();

同樣是使用 loadAnimator 加載對應(yīng)的 xml 動畫。然后使用 animator.setTarget(mTv1);綁定上動畫目標(biāo)。因為在 xml 中,沒有設(shè)置目標(biāo)的參數(shù),所以我們必須通過代碼將目標(biāo)控件與動畫綁定。

(2)、使用示例

我們先寫一個動畫的 xml:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:propertyName="TranslationY"
                android:duration="2000"
                android:valueFrom="0.0"
                android:valueTo="400.0"
                android:interpolator="@android:anim/accelerate_interpolator"
                android:valueType="floatType"
                android:repeatCount="1"
                android:repeatMode="reverse"
                android:startOffset="2000"/>

在這個 xml 中,我們定義了更改屬性為 TranslationY,即改變縱坐標(biāo);時長為 2000 毫秒。從 0 變到 400;使用的插值器是加速插值器,對應(yīng)的值類型為 float 類型。 有些同學(xué)可能會問,為什么是 float 類型,因為 setTranslationY 函數(shù)的參數(shù)是 float 類型的,聲明如下:

public void setTranslationY(float translationY)

最后是設(shè)置重復(fù)次數(shù)和重復(fù)模式。將動畫激活延時設(shè)置為 2000 毫秒; 然后是加載動畫:

ObjectAnimator animator = (ObjectAnimator) AnimatorInflater.loadAnimator(MyActivity.this,
        R.animator.object_animator);
animator.setTarget(mTv1);
animator.start();

效果圖如下:

http://wiki.jikexueyuan.com/project/android-animation/images/108.gif" alt="" />

在點擊后,延時 2000 毫秒后,開始運行。逆序重復(fù)運行一次。 源碼在文章底部給出

(3)、使用 color 屬性示例

這里我們就演示一下如何使用 android:valueFrom、android:valueTo 的 color 屬性用法, 我們建立一個 objectAnimator 的動畫文件:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:propertyName="BackgroundColor"
                android:duration="5000"
                android:valueFrom="#ffff00ff"
                android:valueTo="#ffffff00"/>

設(shè)置屬性名為 BackgroundColor,即對應(yīng)的 set 函數(shù)為 setBackgroundColor(int color); android:valueFrom 和 android:valueTo 的取值都為顏色值,即#開頭的八位數(shù)值;即 ARGB 值; 使用方法不變:

ObjectAnimator animator = (ObjectAnimator) AnimatorInflater.loadAnimator(MyActivity.this,
        R.animator.color_animator);
animator.setTarget(mTv1);
animator.start();

效果圖如下:

http://wiki.jikexueyuan.com/project/android-animation/images/109.gif" alt="" />

從效果圖中可以看到,雖然實現(xiàn)了顏色變化,但會一直閃;所以直接利用 xml 實現(xiàn)的動畫效果并不怎么好,所以如果想要實現(xiàn)顏色變化,還是利用代碼來實現(xiàn)吧。前面的文章中,我們已經(jīng)講過如何利用 ValueAnimator 和 ObjectAnimator 來實現(xiàn)顏色過渡和原理了。大家可以翻看下。 源碼在文章底部給出

3、set

(1)字段意義及使用方法

這個是 AnimatorSet 所對應(yīng)的標(biāo)簽。它只有一個屬性:

<set
  android:ordering=["together" | "sequentially"]>

android:ordering:表示動畫開始順序。together 表示同時開始動畫,sequentially 表示逐個開始動畫; 加載方式為:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(MyActivity.this,
        R.animator.set_animator);
set.setTarget(mTv1);
set.start();

同樣是通過 loadAnimator 加載動畫,然后將其強轉(zhuǎn)為 AnimatorSet;

(2)、示例

在 res/animator 文件夾下新建一個文件(set_animator.xml):

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:ordering="together">
    <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueFrom="0"
            android:valueTo="400"
            android:valueType="floatType"/>
    <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueFrom="0"
            android:valueTo="300"
            android:valueType="floatType"/>
</set>

這里有兩個 objectAnimator 動畫,一個改變值 x 坐標(biāo),一個改變值 y 坐標(biāo);取值分別為 0-400 和 0-300; 然后在代碼中加載:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(MyActivity.this,
        R.animator.set_animator);
set.setTarget(mTv1);
set.start();

動畫效果如下:

http://wiki.jikexueyuan.com/project/android-animation/images/110.gif" alt="" />

源碼在文章底部給出

4、總結(jié)

最后總結(jié)一下,所有 animator 標(biāo)簽及取值范圍如下:

<set
  android:ordering=["together" | "sequentially"]>

    <objectAnimator
        android:propertyName="string"
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <animator
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <set>
        ...
    </set>
</set>

各字段的取值意義在上面講解時已經(jīng)給出,大家可以翻回去看看。

二、開篇示例——AnimatorSet 應(yīng)用

在講完了 XML 使用方法之后,AnimatorSet 的部分就完全結(jié)束了,下面我們就利用學(xué)到的知識來看一下開篇時的那個效果是如何實現(xiàn)的吧。

http://wiki.jikexueyuan.com/project/android-animation/images/111.gif" alt="" />

我們先來分析下這個效果,在用戶點擊按鈕時,把菜單彈出來;彈出來的時候,動畫一點從小變到大,一邊透明度從 0 變到 1.關(guān)鍵問題是,怎么樣實現(xiàn)各個菜單以當(dāng)前點擊按鈕為圓心排列在圓形上;

1、原理

在開始寫代碼之前,我們先講講,如何根據(jù)圓半徑來定位每個圖片的位置,先看下圖:

http://wiki.jikexueyuan.com/project/android-animation/images/20.png" alt="" />

在上面的圖中,我們可以清晰的看到,假如當(dāng)前菜單與 Y 軸的夾角是 a 度,那么這個菜單所移動的 X 軸距離為 radius sin(a);Y 軸的移動距離為 radius cos(a); 這是非常簡單的三角函數(shù)的計算。想必這塊大家理解起來是沒有問題的。 那么第一個問題來了,這個夾角 a 是多少度呢? 很顯然,這里所有的菜單的夾角之和是 90 度。我們總共有五個菜單項,把 90 度夾角做了 4 等分。所以夾角 a 的度數(shù)為 90/4 = 22;所以這五個菜單,第一個菜單的夾角是 0 度,第二個菜單的夾角是 22 度,第三個菜單的夾角是 222 度,第四個夾角是 223 度,第五個的夾角是 224 度. 我們假設(shè) index 表示當(dāng)前菜單的位置索引,從 0 開始,即第一個菜單的索引是 0,第二個菜單的索引是 1,第三個菜單的索引是 2……,而當(dāng)前的菜單與 y 軸的夾角恰好占了 22 度的 index 份;所以當(dāng)前菜單與 Y 軸的夾角為 22 index;這個公式非常重要,大家在這里一定要理解,下面代碼中會用到。 第二個問題來了,如何求對應(yīng)角度的 sin,cos 值 想必很多同學(xué)都知道,JAVA 中有一個 Math 類,它其中有四個函數(shù):

/**
 * 求對應(yīng)弧度的正弦值
 */
double sin(double d)
/**
 * 求對應(yīng)弧度的余弦值
 */
double cos(double d)
/**
 * 求對應(yīng)弧度的正切值
 */
double tan(double d)

這里要非常注意的是,這三個函數(shù)的輸入?yún)?shù)不是度數(shù),而是對應(yīng)的度數(shù)的弧度值! 角度與其對應(yīng)的弧度值對應(yīng)關(guān)系如下:

http://wiki.jikexueyuan.com/project/android-animation/images/21.png" alt="" />

在 Math 中有兩種方法可以得到弧度值: 第一種方法:在 Math 中,Math.PI 不僅代表圓周率π,也代表 180 度角所對應(yīng)的弧度值。所以 Math.sin(Math.PI)就表示 180 度的正弦值,Math.sin(Math.PI/2)就表示 90 度的正弦值。 第二種方法:根據(jù)度數(shù)獲得弧度值 在 Math 中也提供了一個方法

/**
 * Math 中根據(jù)度數(shù)得到弧度值的函數(shù)
 */
double toRadians(double angdeg)

這個函數(shù)就是 Math 中根據(jù)度數(shù)得到弧度值的函數(shù),參數(shù) angdeg 指度數(shù),返回值是對應(yīng)的弧度值。 所以比如我們要求 22 度對應(yīng)的弧度值就是 Math.toRadians(22);所以如果我們要求 22 度所對應(yīng)的正弦值就是 Math.sin(Math.toRadians(22)) 在講了如何根據(jù)半徑求得每個菜單項的位置之后,我們來看看示例工程的代碼。

2、布局代碼(main.xml)

布局代碼很簡單,就是利用 FrameLayout 將所有的菜單都蓋在按鈕的下面,效果圖如下:

http://wiki.jikexueyuan.com/project/android-animation/images/22.png" alt="" />

對應(yīng)代碼為:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_marginBottom="10dp"
             android:layout_marginRight="10dp">

    <Button
            android:id="@+id/menu"
            style="@style/MenuStyle"
            android:background="@drawable/menu"/>

    <Button
            android:id="@+id/item1"
            style="@style/MenuItemStyle"
            android:background="@drawable/circle1"
            android:visibility="gone"/>

    <Button
            android:id="@+id/item2"
            style="@style/MenuItemStyle"
            android:background="@drawable/circle2"
            android:visibility="gone"/>

    <Button
            android:id="@+id/item3"
            style="@style/MenuItemStyle"
            android:background="@drawable/circle3"
            android:visibility="gone"/>

    <Button
            android:id="@+id/item4"
            style="@style/MenuItemStyle"
            android:background="@drawable/circle4"
            android:visibility="gone"/>

    <Button
            android:id="@+id/item5"
            style="@style/MenuItemStyle"
            android:background="@drawable/circle5"
            android:visibility="gone"/>

</FrameLayout>

其中的 style 代碼為:

<resources>
    <style name="MenuStyle">
        <item name="android:layout_width">50dp</item>
        <item name="android:layout_height">50dp</item>
        <item name="android:layout_gravity">right|bottom</item>
    </style>

    <style name="MenuItemStyle">
        <item name="android:layout_width">45dp</item>
        <item name="android:layout_height">45dp</item>
        <item name="android:layout_gravity">right|bottom</item>
    </style>
</resources>

布局是沒什么難度的,下面我們就來看看 MyActivity 中的處理。

3、MyActivity.java

(1)、先看看框架部分:

public class MyActivity extends Activity implements View.OnClickListener{
    private static final String TAG = "MainActivity";

    private Button mMenuButton;
    private Button mItemButton1;
    private Button mItemButton2;
    private Button mItemButton3;
    private Button mItemButton4;
    private Button mItemButton5;

    private boolean mIsMenuOpen = false;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initView();
    }

    private void initView() {
        mMenuButton = (Button) findViewById(R.id.menu);
        mMenuButton.setOnClickListener(this);

        mItemButton1 = (Button) findViewById(R.id.item1);
        mItemButton1.setOnClickListener(this);

        mItemButton2 = (Button) findViewById(R.id.item2);
        mItemButton2.setOnClickListener(this);

        mItemButton3 = (Button) findViewById(R.id.item3);
        mItemButton3.setOnClickListener(this);

        mItemButton4 = (Button) findViewById(R.id.item4);
        mItemButton4.setOnClickListener(this);

        mItemButton5 = (Button) findViewById(R.id.item5);
        mItemButton5.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v == mMenuButton) {
            if (!mIsMenuOpen) {
                mIsMenuOpen = true;
                doAnimateOpen(mItemButton1, 0, 5, 300);
                doAnimateOpen(mItemButton2, 1, 5, 300);
                doAnimateOpen(mItemButton3, 2, 5, 300);
                doAnimateOpen(mItemButton4, 3, 5, 300);
                doAnimateOpen(mItemButton5, 4, 5, 300);
            } else {
                mIsMenuOpen = false;
                doAnimateClose(mItemButton1, 0, 5, 300);
                doAnimateClose(mItemButton2, 1, 5, 300);
                doAnimateClose(mItemButton3, 2, 5, 300);
                doAnimateClose(mItemButton4, 3, 5, 300);
                doAnimateClose(mItemButton5, 4, 5, 300);
            }
        } else {
            Toast.makeText(this, "你點擊了" + v, Toast.LENGTH_SHORT).show();
        }
    }
    ………………
}    

這部分代碼很簡單,就是利用 findviewById 來找到每個菜單的實例,然后對他們添加點擊響應(yīng):

public void onClick(View v) {
    if (v == mMenuButton) {
        if (!mIsMenuOpen) {
            mIsMenuOpen = true;
            doAnimateOpen(mItemButton1, 0, 5, 300);
            …………
        } else {
            mIsMenuOpen = false;
            doAnimateClose(mItemButton1, 0, 5, 300);
            …………
        }
    } else {
        Toast.makeText(this, "你點擊了" + v, Toast.LENGTH_SHORT).show();
    }
}

其中彈出主菜單的按鈕是 mMenuButton,當(dāng)點擊 mMenuButton 時,利用 mIsMenuOpen 來標(biāo)識當(dāng)前是否已經(jīng)彈出菜單;如果沒有彈出,則利用 doAnimateOpen(mItemButton1, 0, 5, 300)將 mItemButton1 彈出來;其它按鈕類似。如果已經(jīng)彈出來,則利用 doAnimateClose(mItemButton1, 0, 5, 300);將 mItemButton1 收回。 下面我們就分別來看看 doAnimateOpen()和 doAnimateClose()的代碼;

(2)、doAnimateOpen()——彈出菜單

先貼出完整代碼:

private void doAnimateOpen(View view, int index, int total, int radius) {
    if (view.getVisibility() != View.VISIBLE) {
        view.setVisibility(View.VISIBLE);
    }
    double degree = Math.toRadians(90)/(total - 1) * index;
    int translationX = -(int) (radius * Math.sin(degree));
    int translationY = -(int) (radius * Math.cos(degree));

    AnimatorSet set = new AnimatorSet();
    //包含平移、縮放和透明度動畫
    set.playTogether(
            ObjectAnimator.ofFloat(view, "translationX", 0, translationX),
            ObjectAnimator.ofFloat(view, "translationY", 0, translationY),
            ObjectAnimator.ofFloat(view, "scaleX", 0f, 1f),
            ObjectAnimator.ofFloat(view, "scaleY", 0f, 1f),
            ObjectAnimator.ofFloat(view, "alpha", 0f, 1));
    //動畫周期為 500ms
    set.setDuration(1 * 500).start();
}

我們倒過來看,先看動畫部分:

set.playTogether(
        ObjectAnimator.ofFloat(view, "translationX", 0, translationX),
        ObjectAnimator.ofFloat(view, "translationY", 0, translationY),
        ObjectAnimator.ofFloat(view, "scaleX", 0f, 1f),
        ObjectAnimator.ofFloat(view, "scaleY", 0f, 1f),
        ObjectAnimator.ofFloat(view, "alpha", 0f, 1));

這里構(gòu)造的動畫是利用 translationX 和 translationY 將控件移動到指定位置。同時,scaleX、scaleY、alpha 都從 0 變到 1;最關(guān)鍵的部分是如何得到 translationX 和 translationY 的值。 在這部分的開篇,我們首先講了,如何講了

translationX = radius * sin(a)
translationY = radius * cos(a)

我們來看看在代碼中如何去做的:

double degree = Math.toRadians(90)/(total - 1) * index;
int translationX = -(int) (radius * Math.sin(degree));
int translationY = -(int) (radius * Math.cos(degree));

首先,是求得兩個菜單的夾角,即公式里的 a 值。Math.toRadians(90)/(total - 1)表示 90 度被分成了 total-1 份,其中每一份的弧度值; 我們前面講過,假設(shè)每一份的弧度值是 22 度,那么當(dāng)前菜單與 Y 軸的夾角就是 22 index 度。這里類似,當(dāng)前菜單與 y 軸的弧度值就是 Math.toRadians(90)/(total - 1) index 在求得夾角以后,直接利用 translationX = radius * sin(a)就可以得到 x 軸的移動距離,但又因為菜單是向左移動了 translationX 距離。所以根據(jù)坐標(biāo)系向下為正,向右為正的原則。這里的移動距離 translationX 應(yīng)該是負值。我們需要的 translationY,因為是向上移動,所以也是負值:

int translationX = -(int) (radius * Math.sin(degree));
int translationY = -(int) (radius * Math.cos(degree));

在理解了彈出的部分之后,收回的代碼就好理解了

(3)、doAnimateClose()——收回菜單

收回菜單就是把彈出菜單的動畫反過來,讓它從 translateX,translateY 的位置上回到 0 點,scaleX、scaleY、alpha 的值從 1 變到 0 即可:

private void doAnimateClose(final View view, int index, int total,
                           int radius) {
   if (view.getVisibility() != View.VISIBLE) {
       view.setVisibility(View.VISIBLE);
   }
   double degree = Math.PI * index / ((total - 1) * 2);
   int translationX = -(int) (radius * Math.sin(degree));
   int translationY = -(int) (radius * Math.cos(degree));
   AnimatorSet set = new AnimatorSet();
   //包含平移、縮放和透明度動畫
   set.playTogether(
           ObjectAnimator.ofFloat(view, "translationX", translationX, 0),
           ObjectAnimator.ofFloat(view, "translationY", translationY, 0),
           ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f),
           ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f),
           ObjectAnimator.ofFloat(view, "alpha", 1f, 0f));

   set.setDuration(1 * 500).start();
}

這段代碼是很容易理解的,但我在這里求 degree 的時候,換了一種方法:

 double degree = Math.PI * index / ((total - 1) * 2);

其實這句代碼與上面的

double degree = Math.toRadians(90)/(total - 1) * index;

是同一個意思。 還記得,我們在講原理的時候,提到過 Math.PI 不僅表示圓周率,也表示 180 度所對應(yīng)的弧度。 所以 Math.toRadians(90)就等于 Math.PI/2,這樣,這兩個公式就是完全一樣的了。 源碼在文章底部給出 好了,到這里有關(guān) AnimatorSet 的部分就講完了,下篇給大家講講有關(guān) viewGroup 動畫相關(guān)的知識。

源碼內(nèi)容: 1、BlogXMLAnimator:第一部分:聯(lián)合動畫的 XML 實現(xiàn)對應(yīng)源碼 2、BlogAnimatorSetDemo:第二部分:開篇示例——AnimatorSet 應(yīng)用對應(yīng)源碼

如果本文有幫到你,記得加關(guān)注哦 源碼下載地址: csdn:http://download.csdn.net/detail/harvic880925/9448719 github: 請大家尊重原創(chuàng)者版權(quán),轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/harvic880925/article/details/50763286 謝謝