1. 程式人生 > >Android動畫之檢視動畫

Android動畫之檢視動畫

前言

       動畫在Android中是一個相當於重要的知識點,使用場景也很多,炫酷的介面效果少不了動畫來提升,這裡我們就先來說說Android中的動畫,在說Android的動畫之前,我們先來了解一下動畫這個效果的通用含義。

什麼是動畫?

       我們在平常生活中,經常會遇到動畫這個詞,大家也不要想歪了,可不是大家理解的島國xx片哦!,那動畫究竟是什麼吶?在不同的場景中,這個詞的名詞解釋都不一樣,那我們這裡也來討論一下動畫是什麼?

1,動畫就是動起來的畫
2,動畫最基本的一點就是能夠隨一定的時間間隔而變化

       這個定義也是我總結的,感覺還挺契合這個概念,如果大家有異議,請告訴我,反正我也不會改的。

動畫屬性

       動畫的概念已經有了,那通用的動畫有哪些屬性吶,或者說具備哪些屬性,比如我有如下一個動畫:

       從這個動畫我們能夠得出哪些屬性?

描述 結論
作用的物件 traget
持續時間 duration
從哪兒來 from
到哪兒去 to
開始時間 beginTime
重複次數 repeatCount
怎麼去 timeLine

       上面我大致總結了一下公共的屬性,這些屬性針對任何動畫都適用,可能還總結的不夠全,這裡只是起到一個拋磚引玉的作用。

動畫組合

       定義有了,屬性有了,還有啥公共是屬性?那就是動畫的執行效果了,一般有單個動畫,就是一次只執行一個動畫,還有就是組合動畫了,多個動畫可以組合成,序列,並行,串並行動畫。

       對公共的動畫我們大致就先說這麼多,現在開始轉移到我們的主題內容,Android動畫。接下的我們就來說說在Android中的動畫的一些概念和怎麼使用!

Android中的動畫分類

  • 檢視動畫
    • Tween動畫
      • 平移動畫
      • 旋轉動畫
      • 縮放動畫
      • 漸變動畫
    • Frame動畫
      • AnimationDrawable
  • 屬性動畫

檢視動畫

       到這裡,我們終於今天本章的主題了。這裡主要來首先來說說檢視動畫,在下一章中我們會說說屬性動畫。檢視動畫與屬性動畫都同屬於Android,但是實現方式是完全不同的。

注意:檢視動畫只能作用於View,能夠實現的效果也僅限於上述的幾種和他們動畫的組合。

Android中的屬性對應關係

       前面我們總結了動畫的通用屬性,那我們這裡就來看看Android中這些屬性的對應關係:

描述 結論 Android屬性
作用的物件 traget View
持續時間 duration duration
從哪兒來 from fromXXX
到哪兒去 to toXXX
開始時間 beginTime startOffset
重複次數 repeatCount repeactCount
怎麼去 timeLine interpolator

       我們可以看到在Android中,屬性對應的名稱大致跟通用的總結的一致,至少看到名字就能理解意思。因此我們後面在實現動畫的時候就按照這些屬性來配置就ok了。

Tween動畫的結構

  • Animation
    • AlphaAnimation
    • TranslateAnimation
    • RotateAnimation
    • ScaleAnimation
    • AnimationSet
             這裡前四個是單個動畫,後面一個是動畫集,他可以組合前面的動畫。

Android動畫實現

       檢視動畫主要有兩種方式來實現:

1,xml實現,首先在res/anim目錄下定義動畫的xml檔案,之後帶程式碼中用AnimationUtils.loadAnimation()載入
2, 程式碼實現,直接new對應的動畫例項來實現動畫。

       我們來看幾個動畫效果,之後例項進行操作一遍:
這裡寫圖片描述

xml實現

       在res/anim下定義動畫檔案,我們分別定義漸變,平移,旋轉,縮放四個動畫檔案:

       漸變動畫,名稱為alpha.xml

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

       fromAlpha表示動畫開始的透明度,toAlpha表示動畫結束的透明度,1.0表示完全不透明,0.0便是完全透明,兩個都是浮點數,duration便是動畫的持續時間,單位為毫秒,2000毫秒錶示2秒。startOffset表示動畫開始的延遲時間,點選開始1秒後開始執行,單位也是毫秒。

       平移動畫,名稱為translate.xml

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

</translate>

       fromXDelta,fromYDelta表示動畫開始時的x,y座標,toXDelta,toYDelta動畫結束的x,y座標,這裡需要注意的是他可以設定三種類型的值,比如我們設定50:

50: 表示50個畫素,是絕對值
50%: 表示自身大小一半,記住這裡是可以設定%號的,他等於0.5,如果設定0.5表示絕對值0.5
50%p :表示父元素大小的一半,p表示相對於父元素

       縮放動畫,名稱為scale.xml

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
       android:duration="3000"
       android:fromXScale="1"
       android:fromYScale="1"
       android:toYScale="2"
       android:toXScale="2"
       android:pivotX="50%"
       android:pivotY="0%">

</scale>

       fromXScale,fromXScale動畫開始時x,y的縮放大小,toXScale,toXScale:動畫結束的x,y縮放大小,1表示本身大小,數值越大,縮放比例越大,pivotX,piovtY:縮放的中心座標位置,可以設定值與平移動畫可以設定的類似:

50: 表示50個畫素,是絕對值
50%: 表示自身中心位置
50%p :表示父元素的中心位置,p表示相對於父元素

       旋轉動畫,名稱為rotate.xml

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

</rotate>

       fromDegrees動畫開始的角度,toDegress動畫結束的角度,正數為順時針,負數為逆時針,pivotX,piovtY:旋轉的中心座標位置,設定的方式與縮放相似。

       xml檔案定義好後,我們就需要在程式碼中進行使用,可以先定義一個view,之後在介面中find出來,之後對view設定動畫:

private void rotate() {
    Animation rotate = AnimationUtils.loadAnimation(this, R.anim.rotate);
    imageView.startAnimation(rotate);
}

private void scale() {
    Animation scale = AnimationUtils.loadAnimation(this, R.anim.scale);
    imageView.startAnimation(scale);
}

private void translate() {
    Animation translate = AnimationUtils.loadAnimation(this, R.anim.translate);
    imageView.startAnimation(translate);
}

private void alpha() {
    Animation alpha = AnimationUtils.loadAnimation(this, R.anim.alpha);
    imageView.startAnimation(alpha);
}

       在程式碼中,都需要先load動畫,採用AnimationUtils來進行load,之後將load出來的動畫設定到view上就可以了。

程式碼實現

       上面我們採用xml實現四種動畫,接下來我們用程式碼來實現這個四種動畫:

private void rotate() {
    RotateAnimation rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
    rotate.setDuration(3000);
    imageView.startAnimation(rotate);
}

private void scale() {
    ScaleAnimation scale = new ScaleAnimation(1,2, 1, 2, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
    scale.setDuration(3000);
    imageView.startAnimation(scale);
}

private void translate() {
    TranslateAnimation translate = new TranslateAnimation(0, 200, 0, 0);
    translate.setDuration(3000);
    imageView.startAnimation(translate);
}

private void alpha() {
    AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f);
    alpha.setDuration(3000);
    imageView.startAnimation(alpha);
}

       從程式碼可以看出,四種動畫都分別對應了不同的類,已經有四種現場的動畫類可以使用。每次使用只需要new對應的例項,設定不同的引數,引數設定也很簡單。這裡就不在詳細說了。

動畫的開始於結束

       動畫我們已經實現了,但是如果你需要在動畫的執行過程中,做某些操作怎麼辦吶?比如在動畫開始的時候提示一下動畫的開始,在動畫的結束時,我們提示動畫的結束,這我們又能怎麼做吶?

       系統也完全考慮到這個問題,我們可以對動畫設定監聽,可以對動畫設定AnimationListener,如下:

rotate.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                //todo
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                // todo  next anim
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

       設定動畫監聽,在動畫開始結束等時機,系統都會回撥對應的函式,比如onAnimationStart就是告訴動畫開始執行了。這樣我們就可以根據不同的回撥做想要做的事情。

組合動畫

       我們在抽象動畫的公共屬性說過,動畫有單個動畫,還有組合動畫,上面我們所實現的都是單個動畫,那怎麼實現組合動畫,組合動畫又分為序列動畫,並行動畫,或者既有序列又有並行。這些又怎麼實現:

  1. 多個單個動畫來實現
    • 序列動畫,可以定義多個動畫,分別對每一個動畫設定監聽,在上一個動畫結束時,開始下一個動畫,也可以設定動畫的startOffset,下一個動畫的startOffset為上一個動畫的startOffset與duration之和
    • 並行動畫,定義多個動畫,設定同樣的startOffset與duration
      1. 採用AnimationSet來實現,下面我們主要才實現一下該方式

       我們需要實現如下效果的動畫,由於將MP4轉換成gif,效果變得很差,將就著看吧:
這裡寫圖片描述
       這裡也可以用兩種方式實現,xml實現與程式碼實現,這裡萬變不離其宗,只不過是將上面的單個動畫安裝需要的效果組合一下就ok,下面我們用xml來實現一下該動畫:

       定義一個love_flay.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

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

    <rotate
        android:duration="3000"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="3000"
        android:toDegrees="-45"></rotate>

    <scale
        android:duration="3000"
        android:fromXScale="1"
        android:fromYScale="1"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="3000"
        android:toXScale="2"
        android:toYScale="2"></scale>
    <alpha
        android:duration="3000"
        android:fromAlpha="1.0"
        android:startOffset="3000"
        android:toAlpha="0.0"></alpha>


</set>

       程式碼載入上面的定義的xml。

private void loveAnim() {
    Animation animation  = AnimationUtils.loadAnimation(this, R.anim.love_fly);
    love.startAnimation(animation);
}

       這樣我們就實現了上面要求的效果,那程式碼方式又怎麼實現? 首先定義多個單個動畫,之後採用AnimationSet來實現,將定義的動畫都add到set中,比如我們需要實現一個在平移的過程中放大並且逐漸消失的動畫:

private void alpha() {
    AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f);
    alpha.setDuration(3000);

    TranslateAnimation translate = new TranslateAnimation(0, 200, 0, 0);
    translate.setDuration(3000);

    ScaleAnimation scale = new ScaleAnimation(1,2, 1, 2, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
    scale.setDuration(3000);

    AnimationSet set = new AnimationSet(true);
    set.addAnimation(alpha);
    set.addAnimation(translate);
    set.addAnimation(alpha);
    imageView.startAnimation(set);
}

       new AnimationSet(true),true表示是否公用插值器,後面我們會詳細說說插值器。

       這裡我們就已經說完了怎麼實現組合動畫,之後我們來講講插值器是個啥!

插值器

       說道插值器,我們首先要知道插值器是幹什麼用的?

插值器控制動畫在時間軸上的變換效果
       我們先來幾個效果:
這裡寫圖片描述

       我們分別就這幾個效果來做一下說明系統到底是怎麼來控制效果的,首先我們來看看AccelerateDecelerateInterpolator,該插值器是先加速後減速。

       插值器中主要是根據getInterpolation來獲取某一個時間點的值,AccelerateDecelerateInterpolator的呼叫函式為(float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; 輸入範圍為0到1,根據這個函式我們可以生產如下的圖片:

這裡寫圖片描述

       從函式圖可以看出,當輸入在0到1中時,函式先加速後減速,系統就是靠這個來控制動畫的執行效果的。

       我們在來看CycleInterpolator,該插值器有一個震盪的效果,我們來看看他的呼叫函式:(float)(Math.sin(2 * mCycles * Math.PI * input)); mCycles = 2, input( 0, 1),mCycles為2表示震盪兩次,我們來看看函式圖:
這裡寫圖片描述

       可以看到在0到1中迴圈了兩次。

       從上面的可以看到如果你想實現特定的效果,只需要實現特定的插值器就好了。

動畫的最終位置

       上面我們執行了多個動畫,都發現每次執行完成後都回到了初始位置,那我們怎麼才能將動畫停止在最終位置?其實很容易實現該效果,只需要設定動畫的setFillAfter為true就可以了。

動畫的響應

       我們對動畫設定點選事件,程式碼如下:

public class LocationActivity extends AppCompatActivity {

    private ImageView love;

    public static void start(Context context) {
        Intent intent = new Intent();
        intent.setClass(context, LocationActivity.class);
        context.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_location);
        findViews();
        setViewListener();
    }

    private void findViews() {
        love = (ImageView) findViewById(R.id.love);
        love.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(LocationActivity.this, "clicked", Toast.LENGTH_LONG).show();
            }
        });
    }

    private void setViewListener() {
        findViewById(R.id.set).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loveAnim();
            }
        });
    }

    private void loveAnim() {
        TranslateAnimation translate = new TranslateAnimation(0,0,0,-200);
        translate.setDuration(3000);
        translate.setFillAfter(true);
        love.startAnimation(translate);
    }
}

       執行後,發現動畫在最終位置不能點選,而點選事件還是在初始位置。

檢視動畫是不可互動動畫,點選事件還是在初始位置

幀動畫

       我們來看看如下效果:
這裡寫圖片描述
       首先我們需要定義一個動畫檔案,但是該動畫檔案不是放在res/anim下,而是放置drawable下面。
       定義一個play_list.xml:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/play_1" android:duration="50" />
    <item android:drawable="@drawable/play_2" android:duration="50" />
    <item android:drawable="@drawable/play_3" android:duration="50" />
    <item android:drawable="@drawable/play_4" android:duration="50" />
    <item android:drawable="@drawable/play_5" android:duration="50" />
    <item android:drawable="@drawable/play_6" android:duration="50" />
    <item android:drawable="@drawable/play_7" android:duration="50" />
    <item android:drawable="@drawable/play_8" android:duration="50" />
    <item android:drawable="@drawable/play_9" android:duration="50" />
    <item android:drawable="@drawable/play_10" android:duration="50" />
    <item android:drawable="@drawable/play_11" android:duration="50" />
    <item android:drawable="@drawable/play_12" android:duration="50" />
    <item android:drawable="@drawable/play_13" android:duration="50" />
    <item android:drawable="@drawable/play_14" android:duration="50" />
    <item android:drawable="@drawable/play_15" android:duration="50" />
    <item android:drawable="@drawable/play_16" android:duration="50" />
    <item android:drawable="@drawable/play_17" android:duration="50" />
    <item android:drawable="@drawable/play_18" android:duration="50" />

</animation-list>

       之後在程式碼中進行載入:

public class FrameActivity extends AppCompatActivity implements View.OnClickListener {

    private ImageView frame;
    private AnimationDrawable drawable;

    public static void start(Context context) {
        Intent intent = new Intent();
        intent.setClass(context, FrameActivity.class);
        context.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_frame);
        findViews();
        setViewsListener();
    }

    private void findViews() {
        frame = (ImageView) findViewById(R.id.frame);
    }

    private void setViewsListener() {
        findViewById(R.id.start).setOnClickListener(this);
        findViewById(R.id.end).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start:
                startAnim();
                break;
            case R.id.end:
                stopAnim();
                break;
        }
    }

    private void stopAnim() {
        if(drawable!=null){
            if(drawable.isRunning()){
                drawable.stop();
            }
        }
    }

    private void startAnim() {
        if(drawable==null){
            frame.setBackgroundResource(R.drawable.play_list);
            drawable = (AnimationDrawable) frame.getBackground();
            drawable.setOneShot(true);
        }
        if(!drawable.isRunning()){
            drawable.start();
        }
    }
}

       將動畫檔案設定為view的背景,之後在獲取出來為AnimationDrawable,setOneShot如果為true,表示只執行一次,start開始執行動畫,isRunning表示動畫知否在執行,stop停止動畫。其他屬性就不一一解釋了。

總結

       到此檢視動畫就講述完了,下一章來說說屬性動畫。