在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

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

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

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

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

(這里實(shí)現(xiàn)的是一個(gè)動(dòng)畫菜單,在點(diǎn)擊菜單按鈕時(shí),彈出各個(gè)菜單)

一、聯(lián)合動(dòng)畫的 XML 實(shí)現(xiàn)

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

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

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

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

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

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

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

通過(guò) loadAnimator 將 animator 動(dòng)畫的 xml 文件,加載進(jìn)來(lái),根據(jù)類型進(jìn)行強(qiáng)轉(zhuǎn)。

(3)、簡(jiǎn)單示例

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

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

在效果圖中可以看到,我們生成了一個(gè)動(dòng)畫,動(dòng)態(tài)了改變了當(dāng)前控件的坐標(biāo)位置。 我們先在 res/animator 文件夾下生成一個(gè)動(dòng)畫的 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,所以對(duì)應(yīng)的 android:valueFrom、android:valueTo 都必須是 int 類型的值;插值器使用 bounce 回彈插值器 然后看看加載到程序中過(guò)程:

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

2、objectAnimator

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

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

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

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

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

(2)、使用示例

我們先寫一個(gè)動(dòng)畫的 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"/>

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

public void setTranslationY(float translationY)

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

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="" />

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

(3)、使用 color 屬性示例

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

<?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,即對(duì)應(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="" />

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

3、set

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

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

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

android:ordering:表示動(dòng)畫開始順序。together 表示同時(shí)開始動(dòng)畫,sequentially 表示逐個(gè)開始動(dòng)畫; 加載方式為:

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

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

(2)、示例

在 res/animator 文件夾下新建一個(gè)文件(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>

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

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

動(dòng)畫效果如下:

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>

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

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

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

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

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

1、原理

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

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

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

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

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

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

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

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

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

2、布局代碼(main.xml)

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

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

對(duì)應(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>

布局是沒(méi)什么難度的,下面我們就來(lái)看看 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, "你點(diǎn)擊了" + v, Toast.LENGTH_SHORT).show();
        }
    }
    ………………
}    

這部分代碼很簡(jiǎn)單,就是利用 findviewById 來(lái)找到每個(gè)菜單的實(shí)例,然后對(duì)他們添加點(diǎn)擊響應(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, "你點(diǎn)擊了" + v, Toast.LENGTH_SHORT).show();
    }
}

其中彈出主菜單的按鈕是 mMenuButton,當(dāng)點(diǎn)擊 mMenuButton 時(shí),利用 mIsMenuOpen 來(lái)標(biāo)識(shí)當(dāng)前是否已經(jīng)彈出菜單;如果沒(méi)有彈出,則利用 doAnimateOpen(mItemButton1, 0, 5, 300)將 mItemButton1 彈出來(lái);其它按鈕類似。如果已經(jīng)彈出來(lái),則利用 doAnimateClose(mItemButton1, 0, 5, 300);將 mItemButton1 收回。 下面我們就分別來(lái)看看 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();
    //包含平移、縮放和透明度動(dòng)畫
    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));
    //動(dòng)畫周期為 500ms
    set.setDuration(1 * 500).start();
}

我們倒過(guò)來(lái)看,先看動(dòng)畫部分:

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)造的動(dòng)畫是利用 translationX 和 translationY 將控件移動(dòng)到指定位置。同時(shí),scaleX、scaleY、alpha 都從 0 變到 1;最關(guān)鍵的部分是如何得到 translationX 和 translationY 的值。 在這部分的開篇,我們首先講了,如何講了

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

我們來(lái)看看在代碼中如何去做的:

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

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

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

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

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

收回菜單就是把彈出菜單的動(dòng)畫反過(guò)來(lái),讓它從 translateX,translateY 的位置上回到 0 點(diǎn),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();
   //包含平移、縮放和透明度動(dòng)畫
   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 的時(shí)候,換了一種方法:

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

其實(shí)這句代碼與上面的

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

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

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

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