android 動(dòng)畫系列三 屬性動(dòng)畫(PropertyAnimation )

一鸽斟、概述

  • 屬性動(dòng)畫(Property Animation)是在 Android 3.0(API 11)后才提供的一種全新動(dòng)畫模式
  • 屬性動(dòng)畫的出現(xiàn)主要是為了彌補(bǔ)補(bǔ)間動(dòng)畫(Tween Animation)的缺陷
  • 下面來看一下補(bǔ)間動(dòng)畫的缺陷
    1谤绳、 作用對(duì)象的局限
    只能夠作用在視圖View上盟猖,即只可以對(duì)一個(gè)Button适揉、TextView玷室、甚至是LinearLayout,或者其它繼承自View的組件進(jìn)行動(dòng)畫操作,但無法對(duì)非View的對(duì)象進(jìn)行動(dòng)畫操作
    2捕传、 沒有改變View的屬性,只是改變視覺效果
    只在屏幕繪制上的動(dòng)畫扩劝,控件的屬性并沒有改變庸论,一個(gè)經(jīng)典的問題就是一個(gè)Button從一個(gè)地方移動(dòng)到另一個(gè)地方,點(diǎn)擊事件還是在原來的地方
    3今野、拓展性太差
    只能寫移動(dòng)葡公、縮放、旋轉(zhuǎn)条霜、漸變四種動(dòng)畫催什,以及這四種動(dòng)畫的組合,一旦遇到相對(duì)復(fù)雜的動(dòng)畫效果宰睡,即超出了上述4種動(dòng)畫效果蒲凶,那么補(bǔ)間動(dòng)畫則無法實(shí)現(xiàn)

二、特點(diǎn)

  • 可以用在任意java對(duì)象
  • 不只是4種基本變換拆内,還有其他動(dòng)畫效果旋圆,并且可以自定義動(dòng)畫效果

三、工作原理

  • 在一定時(shí)間間隔內(nèi)麸恍,通過不斷對(duì)值進(jìn)行改變灵巧,并不斷將該值賦給對(duì)象的屬性搀矫,從而實(shí)現(xiàn)該對(duì)象在該屬性上的動(dòng)畫效果,注意看這里刻肄,屬性動(dòng)畫的本質(zhì)不再是強(qiáng)調(diào)動(dòng)畫本身了瓤球,而是變成了數(shù)值發(fā)生器,不管你想干什么敏弃,屬性動(dòng)畫只關(guān)心生成數(shù)值數(shù)列給你去使用卦羡,這就是屬性動(dòng)畫拓展性的根本,只關(guān)心數(shù)值的生成
  • 具體的工作原理如下圖


    微信圖片_20180918145035.png
從以上的圖片原理分析有三個(gè)核心類非常重要:AnimatorSet ValueAnimator ObjectAnimator
  • AnimatorSet 屬性動(dòng)畫集合麦到,就是把一堆動(dòng)畫放在一起執(zhí)行绿饵,屬性動(dòng)畫的集合提供了幾種非常靈活的動(dòng)畫執(zhí)行方式。
  • ValueAnimator 屬性動(dòng)畫的核心:數(shù)值發(fā)生器瓶颠,生成數(shù)值序列的類
  • ObjectAnimator 是ValueAnimator的子類拟赊,是對(duì)ValueAnimator操作的封裝,使用起來更簡(jiǎn)便粹淋,一般都是用來對(duì)某一個(gè)對(duì)象的屬性做動(dòng)畫的要门。

四、 ValueAnimator

ValueAnimator 是屬性動(dòng)畫的根基了廓啊,是應(yīng)用最廣廣泛的了,尤其是在自定義 view 中封豪,ValueAnimator類中有幾個(gè)核心的方法

  • ValueAnimator ofInt (int... values):返回一個(gè)int型變化的ValueAnimator
    (采用默認(rèn)的整型估值器(IntEvaluator))
  • ValueAnimator ofFloat (float... values):返回一個(gè)float型變化的ValueAnimator
    (采用默認(rèn)的浮點(diǎn)型估值器 (FloatEvaluator))
  • ValueAnimator ofArgb (int... values):返回一個(gè)顏色值變化的ValueAnimator谴轮,API LEVEL 21引入。
    (采用默認(rèn)的顏色估值器 (ArgbEvaluator)
    以上三個(gè)構(gòu)造方法如果傳入了3個(gè)參數(shù) a,b,c ,則是先從a平滑過渡到b,再?gòu)腷平滑過渡到C吹埠,以此類推
  • ValueAnimator ofObject (TypeEvaluator evaluator, Object... values):返回一個(gè)object型變化的ValueAnimator第步。
    (采用自定義對(duì)象估值器 (TypeEvaluator))
    從以上方法的定義上看,主要區(qū)別是四種不同類型的估值器達(dá)到返回不同效果的數(shù)值
    還是老一套缘琅,先看使用方法和效果粘都, 使用的方式 分為 XML 設(shè)置 / Java 代碼設(shè)置
4-1、ValueAnimator ofInt (int... values)
方式一刷袍、java代碼設(shè)置
 //設(shè)置動(dòng)畫的初始值和結(jié)束值
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 5);
        //設(shè)置動(dòng)畫的時(shí)間
        valueAnimator.setDuration(500);
        //設(shè)置延時(shí)播放
        valueAnimator.setStartDelay(500);
        //設(shè)置動(dòng)畫播放重復(fù)次數(shù),ValueAnimator.INFINITE=無限重復(fù)
        valueAnimator.setRepeatCount(0);
        //設(shè)置動(dòng)畫播放模式 ValueAnimator.RESTART=正序播放   ValueAnimator.REVERSE=倒敘播放
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        //將改變的值手動(dòng)付給對(duì)象的屬性值:通過動(dòng)畫的更新監(jiān)聽器實(shí)現(xiàn)
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int animatedValue = (int) animation.getAnimatedValue();
                Log.d("DXDD", animatedValue + "");
            }
        });
        valueAnimator.start();
效果圖
微信圖片_20180918164044.png

從上面可以看出數(shù)值是從0-5的變化翩隧,可以看出并不是均勻分布,呻纹,這就和插值器有關(guān)系了堆生,,看下面源碼

    // The time interpolator to be used if none is set on the animation
    private static final TimeInterpolator sDefaultInterpolator =
            new AccelerateDecelerateInterpolator();

通過源碼看出插值器的速率是始末速率較慢雷酪,中間加速淑仆,如果需要改變動(dòng)畫速率可以通過改變插值器的方法進(jìn)行。從上可以看出插值器和估值器的聯(lián)系哥力≌岬。看下圖分析


微信圖片_20180918180239.png
方式二、xml代碼設(shè)置
  • 步驟1:在路徑 res/animator的文件夾里創(chuàng)建相應(yīng)的動(dòng)畫 .xml文件
  • 步驟2:設(shè)置動(dòng)畫參數(shù)
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:valueFrom="0"
    android:valueTo="5"
    android:valueType="intType"

    android:duration="500"
    android:startOffset="500"
    android:repeatCount="0"
    android:repeatMode="restart"
    android:fillBefore="true"
    android:interpolator="@android:anim/overshoot_interpolator"
    />
  • 使用方法
    ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(PropertyAnimaActivity.this, R.animator.values_int);
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        int animatedValue = (int) animation.getAnimatedValue();
                        Log.d("DXDD", animatedValue + "");
                    }
                });
                animator.start();
效果圖同上,下面結(jié)合手動(dòng)賦值給對(duì)象屬性做個(gè)實(shí)例看看效果
  • 實(shí)現(xiàn)的動(dòng)畫效果:圖片寬高放大一倍
 //設(shè)置動(dòng)畫的初始值和結(jié)束值(圖片寬度放大一倍)
        ValueAnimator valueAnimator = ValueAnimator.ofInt(img_girl.getLayoutParams().width, img_girl.getLayoutParams().width * 2);
        //設(shè)置動(dòng)畫的時(shí)間
        valueAnimator.setDuration(1000);
        //設(shè)置動(dòng)畫播放重復(fù)次數(shù),ValueAnimator.INFINITE=無限重復(fù)
        valueAnimator.setRepeatCount(0);
        //設(shè)置動(dòng)畫播放模式 ValueAnimator.RESTART=正序播放   ValueAnimator.REVERSE=倒敘播放
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        //設(shè)置插值器
        valueAnimator.setInterpolator(new LinearInterpolator());
        //將改變的值手動(dòng)付給對(duì)象的屬性值:通過動(dòng)畫的更新監(jiān)聽器實(shí)現(xiàn)
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 每次值變化時(shí)寞射,將值手動(dòng)賦值給對(duì)象的屬性
                img_girl.getLayoutParams().width = (int) animation.getAnimatedValue();
                //刷新視圖渔工,即重新繪制,實(shí)現(xiàn)動(dòng)畫效果
                img_girl.requestLayout();
            }
        });
        valueAnimator.start();

 //設(shè)置動(dòng)畫的初始值和結(jié)束值(圖片高度放大一倍)
        ValueAnimator valueAnimator = ValueAnimator.ofInt(img_girl.getLayoutParams().height, img_girl.getLayoutParams().height * 2);
        //設(shè)置動(dòng)畫的時(shí)間
        valueAnimator.setDuration(1000);
        //設(shè)置動(dòng)畫播放重復(fù)次數(shù),ValueAnimator.INFINITE=無限重復(fù)
        valueAnimator.setRepeatCount(2);
        //設(shè)置動(dòng)畫播放模式 ValueAnimator.RESTART=正序播放   ValueAnimator.REVERSE=倒敘播放
        valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
        //設(shè)置插值器
        valueAnimator.setInterpolator(new LinearInterpolator());
        //將改變的值手動(dòng)付給對(duì)象的屬性值:通過動(dòng)畫的更新監(jiān)聽器實(shí)現(xiàn)
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 每次值變化時(shí)怠惶,將值手動(dòng)賦值給對(duì)象的屬性
                img_girl.getLayoutParams().height = (int) animation.getAnimatedValue();
                //刷新視圖涨缚,即重新繪制,實(shí)現(xiàn)動(dòng)畫效果
                img_girl.requestLayout();
            }
        });
        valueAnimator.start();
效果圖
girl.gif

合并是兩種動(dòng)畫同事播放策治,贯城,感覺代碼有點(diǎn)重復(fù)使用吧,二拐,學(xué)習(xí)完下面的內(nèi)容會(huì)有更簡(jiǎn)單的實(shí)現(xiàn)方法

4-2驰吓、ValueAnimator ofFloat (float... values)使用方法和效果同上,不同的地方是監(jiān)聽返回值是float類型履腋,返回的類型不同主要是因?yàn)槭褂昧瞬煌墓乐灯?估值器在下面會(huì)詳細(xì)介紹)
4-3珊燎、ValueAnimator ofArgb (int... values)

ValueAnimator中的ofArgb()可以幫助我們實(shí)現(xiàn)顏色的漸變效果,Google在API LEVEL 21之后增加了這個(gè)方法ofArgb()遵湖。通過這個(gè)方法我們更容易地實(shí)現(xiàn)顏色演變悔政,通過ofArgb和ArgbEvaluator,我們可以輕松實(shí)現(xiàn)顏色漸變效果

java代碼
 //第一種方法
        //設(shè)置初始值過度值和結(jié)束值
        ValueAnimator argbAnim = ValueAnimator.ofArgb(Color.parseColor("#ff669900"), Color.parseColor("#ff0099cc"), Color.parseColor("#ffff4444"), Color.parseColor("#ff669900"));
       //設(shè)置動(dòng)畫時(shí)間
        argbAnim.setDuration(2000);
        //設(shè)置勻速插值器
        argbAnim.setInterpolator(new LinearInterpolator());
        //動(dòng)畫監(jiān)聽
        argbAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                btn_Argb.setBackgroundColor((Integer) animation.getAnimatedValue());
            }
        });
        argbAnim.start();
//第二種方法
        //設(shè)置初始值過度值和結(jié)束值
        ValueAnimator argbAnim = ValueAnimator.ofInt(Color.parseColor("#ff669900"), Color.parseColor("#ff0099cc"), Color.parseColor("#ffff4444"), Color.parseColor("#ff669900"));
        //設(shè)置動(dòng)畫時(shí)間
        argbAnim.setDuration(2000);
        //設(shè)置估值器
        argbAnim.setEvaluator(new ArgbEvaluator());
        //設(shè)置勻速插值器
        argbAnim.setInterpolator(new LinearInterpolator());
        //動(dòng)畫監(jiān)聽
        argbAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                btn_Argb.setBackgroundColor((Integer) animation.getAnimatedValue());
            }
        });
        argbAnim.start();
效果圖如下:(需要向下兼容用第二張方法即可實(shí)現(xiàn))
argb.gif
4-4延旧、ValueAnimator ofObject (TypeEvaluator evaluator, Object... values)

ofObject方法是什么意思呢谋国?我們都知道ofInt和ofFloat都是針對(duì)Int值和Float值的變化,但是迁沫,我們只能控制一個(gè)值的變化芦瘾,但是當(dāng)我們需要實(shí)現(xiàn)多值變化時(shí),它們就不再滿足我們的需求集畅。因?yàn)閮蓚€(gè)屬性的初始值和結(jié)束值不一樣近弟,那么我們就可以將兩個(gè)屬性值封裝到一個(gè)對(duì)象里面,那么初始值的object和結(jié)束值的object就可以包含兩個(gè)屬性不同的初始值和結(jié)束值了挺智,這個(gè)時(shí)候就需要自定義估值TypeEvaluator器達(dá)到效果祷愉。介紹完估值器在進(jìn)行ValueAnimator ofObject示例練習(xí)

五、估值器 (TypeEvaluator)

  • 定義:一個(gè)接口
  • 作用:設(shè)置 屬性值 從初始值過渡到結(jié)束值 的變化具體數(shù)值
  • 應(yīng)用場(chǎng)景: 協(xié)助插值器 實(shí)現(xiàn)非線性運(yùn)動(dòng)的動(dòng)畫效果
  • 計(jì)算方式:根據(jù) 插值器計(jì)算出當(dāng)前屬性值改變的百分比 & 初始值 & 結(jié)束值 來計(jì)算 當(dāng)前屬性具體的數(shù)值
    (如:動(dòng)畫進(jìn)行了50%(初始值=100逃贝,結(jié)束值=200 )谣辞,那么勻速插值器計(jì)算出了當(dāng)前屬性值改變的百分比是50%,估值器則負(fù)責(zé)計(jì)算當(dāng)前屬性值 = 100 + (200-100)x50% = 150.)=初始值+動(dòng)畫百分比*(結(jié)束值-初始值)
系統(tǒng)默認(rèn)提供了四種估值器沐扳,如果達(dá)不到要求那么就需要我們自定義估值器泥从,需要自定義估值器就需要實(shí)現(xiàn)TypeEvaluator接口,復(fù)寫evaluate()
public interface TypeEvaluator<T> {
    /**
     *
     * @param fraction  插值器getInterpolation()的返回值 
     * @param startValue 起始值.
     * @param endValue   結(jié)束值.
     * @return  T   賦給動(dòng)畫屬性的具體數(shù)值
     *    
     */
    public T evaluate(float fraction, T startValue, T endValue);

前面介紹過插值器與估值器的關(guān)系沪摄,其實(shí)就事插值器中input 和 fraction關(guān)系:input的值決定了fraction的值:input值經(jīng)過計(jì)算后傳入到插值器的getInterpolation()躯嫉,然后通過實(shí)現(xiàn)getInterpolation()中的邏輯算法纱烘,根據(jù)input值來計(jì)算出一個(gè)返回值,而這個(gè)返回值就是fraction祈餐,那么先來看一個(gè)系統(tǒng)的插值器:浮點(diǎn)型插值器:FloatEvaluator

public class FloatEvaluator implements TypeEvaluator<Number> {
    // FloatEvaluator實(shí)現(xiàn)了TypeEvaluator接口

    // 重寫evaluate()
    public Float evaluate(float fraction, Number startValue, Number endValue) {
     // fraction:表示動(dòng)畫完成度(根據(jù)它來計(jì)算當(dāng)前動(dòng)畫的值)
    // startValue擂啥、endValue:動(dòng)畫的初始值和結(jié)束值
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
      //計(jì)算方式:初始值+動(dòng)畫百分比*(結(jié)束值-初始值)
    }
  • 系統(tǒng)默認(rèn)的估值器都有對(duì)應(yīng)的計(jì)算邏輯,但對(duì)于ValueAnimator.ofObject()帆阳,并沒有系統(tǒng)默認(rèn)實(shí)現(xiàn)哺壶,因?yàn)閷?duì)對(duì)象的動(dòng)畫操作復(fù)雜 & 多樣,系統(tǒng)無法知道如何從初始對(duì)象過度到結(jié)束對(duì)象蜒谤,因此我們需自定義估值器(TypeEvaluator)來告知系統(tǒng)如何進(jìn)行從 初始對(duì)象 過渡到 結(jié)束對(duì)象的邏輯山宾。下面示例實(shí)現(xiàn)一個(gè)加入購(gòu)物車效果的估值器:
效果圖
buy.gif
  • 如果需要達(dá)到拋物線效果,就需要自定義貝塞爾曲線數(shù)值變化的估值器

public class PointBezierTypeEvaluator implements TypeEvaluator<PointF> {
    PointF control;
    PointF mPointF = new PointF();

    public PointBezierTypeEvaluator(PointF control) {
        this.control = control;
    }
    @Override
    public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
        return getBezierPoint(startValue, endValue, control, fraction);
    }

    /**
     * 二次貝塞爾曲線公式
     *
     * @param start   開始的數(shù)據(jù)點(diǎn)
     * @param end     結(jié)束的數(shù)據(jù)點(diǎn)
     * @param control 控制點(diǎn)
     * @param t       float 0-1
     * @return 不同t相應(yīng)的PointF
     */
    private PointF getBezierPoint(PointF start, PointF end, PointF control, float t) {
        mPointF.x = (1 - t) * (1 - t) * start.x + 2 * t * (1 - t) * control.x + t * t * end.x;
        mPointF.y = (1 - t) * (1 - t) * start.y + 2 * t * (1 - t) * control.y + t * t * end.y;
        return mPointF;
    }
  • java實(shí)現(xiàn)方式

    private void clickView(View view) {
        int[] clickCoordinate = new int[2];
        int[] parentCoordinate = new int[2];
        int[] buyCoordinate = new int[2];
        //分別獲取被點(diǎn)擊view鳍徽、父布局资锰、購(gòu)物車在屏幕上的坐標(biāo)
        view.getLocationOnScreen(clickCoordinate);
        shopping_container.getLocationOnScreen(parentCoordinate);
        img_buy.getLocationOnScreen(buyCoordinate);

        //創(chuàng)建滑動(dòng)的imageview并添加到父布局中
        if (moveImg == null) {
            moveImg = new ImageView(this);
            moveImg.setImageResource(R.drawable.red_ball);
            shopping_container.addView(moveImg);
        }
        moveImg.setVisibility(View.VISIBLE);
        moveImg.setX(clickCoordinate[0] - parentCoordinate[0]);
        moveImg.setY(clickCoordinate[1] - parentCoordinate[1]);

        //計(jì)算滑動(dòng)imageview的三個(gè)控制點(diǎn)(起點(diǎn)、結(jié)束點(diǎn)阶祭、控制點(diǎn))
        PointF startP = new PointF();
        PointF endP = new PointF();
        PointF controlP = new PointF();
        startP.x = clickCoordinate[0] - parentCoordinate[0];
        startP.y = clickCoordinate[1] - parentCoordinate[1];
        endP.x = buyCoordinate[0] - parentCoordinate[0] + img_buy.getLayoutParams().width / 5;
        endP.y = buyCoordinate[1] - parentCoordinate[1] + img_buy.getLayoutParams().width / 5;
        controlP.x = endP.x;
        controlP.y = startP.y;

        //執(zhí)行動(dòng)畫
        ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointBezierTypeEvaluator(controlP), startP, endP);
        valueAnimator.setDuration(1000);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                                            @Override
                                            public void onAnimationUpdate(ValueAnimator animation) {
                                                PointF pointF = (PointF) animation.getAnimatedValue();
                                                moveImg.setY(pointF.y);
                                                moveImg.setX(pointF.x);
                                            }
                                        }
        );
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                moveImg.setVisibility(View.INVISIBLE);
                ++count;
                tv_num.setText(count + "");
                ScaleImage(img_buy);
            }
        });
        valueAnimator.start();
    }
  • 購(gòu)物車動(dòng)畫效果
  private void ScaleImage(ImageView img) {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(img, "scaleX", 1f, 0.5f, 1f);
        objectAnimator.setDuration(500);
        objectAnimator.setInterpolator(new SpringInterpolator());
        objectAnimator.start();
        ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(img, "scaleY", 1f, 0.5f, 1f);
        objectAnimator1.setDuration(500);
        objectAnimator1.setInterpolator(new SpringInterpolator());
        objectAnimator1.start();
    }
  • 以上就是自定義估值器結(jié)合ValueAnimator.ofObject()實(shí)現(xiàn)購(gòu)物車拋物線效果
  • 從購(gòu)物車動(dòng)畫效果代碼看出 ObjectAnimator這個(gè)類的動(dòng)畫實(shí)現(xiàn)方式绷杜,下面詳細(xì)介紹一下這個(gè)類

六、ObjectAnimator

上面說了ObjectAnimator是ValueAnimator的子類濒募,是對(duì)ValueAnimator操作的封裝鞭盟,目的就是讓我們的屬性動(dòng)畫操作更加簡(jiǎn)單,這符合 java 簡(jiǎn)單化的思想瑰剃,大伙仔細(xì)想想懊缺,簡(jiǎn)單好用就是我們碼代碼的終極追求之一啊,下面來看看常用屬性和用法:

6-1 常用的propertyName:
  • rotationX 圍繞x軸旋轉(zhuǎn)
  • rotationY 圍繞y軸旋轉(zhuǎn)
  • rotation 圍繞軸點(diǎn)旋轉(zhuǎn)
  • translationX 在x軸方向上平移
  • translationY 在y軸方向上平移
  • scaleX 在x軸方向縮放
  • scaleY 在y軸方向縮放
  • alpha 透明度
  • width 寬度
  • height 高度
6-2 使用方法:
ObjectAnimator animator = ObjectAnimator.ofFloat(testView, "translationX", 100);
animator.setDuration(600);
animator.setRepeatMode(ObjectAnimator.REVERSE);
animator.setRepeatCount(1);
animator.start();
  • 這是一個(gè)位移動(dòng)畫培他,像右移動(dòng)100px,需要達(dá)到其他的效果,方法同上遗座,只需修改動(dòng)畫屬性即可舀凛。

七、path動(dòng)畫

5.0版本屬性動(dòng)畫中添加了一個(gè)path動(dòng)畫途蒋,可以讓 view 沿著 path 的路徑做動(dòng)畫
  • 代碼
  ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, View.X, View.Y, path_view.getPath());
                animator.setDuration(1000);
                animator.setRepeatMode(ObjectAnimator.REVERSE);
                animator.setRepeatCount(1);
                animator.start();
  • 有4個(gè)參數(shù)猛遍,第一個(gè)是目標(biāo) view,后面2個(gè)是需要操作的 view 的位置坐標(biāo)号坡,最后是 path
  • 效果圖


    path.gif

八懊烤、AnimatorSet 動(dòng)畫集合

8-1在AnimatorSet中給為我們提供了兩個(gè)方法playSequentially和playTogether
  • playSequentially表示所有動(dòng)畫依次播放
  • playTogether表示所有動(dòng)畫一起開始
    playTogether和playSequentially在激活動(dòng)畫后,控件的動(dòng)畫情況與它們無關(guān)宽堆,它們只負(fù)責(zé)定時(shí)激活控件動(dòng)畫腌紧。 playSequentially只有上一個(gè)控件做完動(dòng)畫以后,才會(huì)激活下一個(gè)控件的動(dòng)畫畜隶,如果上一控件的動(dòng)畫是無限循環(huán)壁肋,那下一個(gè)控件就無法做動(dòng)畫了号胚。
  • 使用示例:
ObjectAnimator objectAnimator01 = ObjectAnimator.ofArgb(ivImage05, "BackgroundColor",
                getResources().getColor(R.color.colorPrimary),
                getResources().getColor(R.color.colorAccent),
                getResources().getColor(R.color.colorPrimary));
        ObjectAnimator objectAnimator02 = ObjectAnimator.ofFloat(ivImage05, "TranslationY", 0, 300, 0);
        ObjectAnimator objectAnimator03 = ObjectAnimator.ofFloat(ivImage06, "TranslationY", 0, 400, 0);
        ObjectAnimator objectAnimator04 = ObjectAnimator.ofFloat(ivImage07, "TranslationY", 0, 500, 0);
        ObjectAnimator objectAnimator05 = ObjectAnimator.ofFloat(ivImage08, "TranslationY", 0, 600, 0);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(objectAnimator01, objectAnimator02, objectAnimator03, objectAnimator04, objectAnimator05);
        animatorSet.setDuration(2000);
        animatorSet.start();
8-2 AnimatorSet.Builder(自由設(shè)置動(dòng)畫順序)

AnimatorSet.Builder用于實(shí)現(xiàn)playTogether和playSequentially無法實(shí)現(xiàn)的效果,可以實(shí)現(xiàn)非常自由的組合動(dòng)畫浸遗,比如有三個(gè)動(dòng)畫A,B,C想先播放C然后同時(shí)播放A和B猫胁,利用playTogether和playSequentially是沒辦法實(shí)現(xiàn)的,但利用AnimatorSet.Builder卻可以輕易實(shí)現(xiàn)跛锌。

AnimatorSet.Builder常用方法:
方法 說明
public Builder play(Animator anim) 表示要播放哪個(gè)動(dòng)畫 AnimatorSet中的play方法是獲取AnimatorSet.Builder對(duì)象的唯一途徑
public Builder with(Animator anim) 和前面動(dòng)畫一起執(zhí)行
public Builder before(Animator anim) 執(zhí)行前面的動(dòng)畫后才執(zhí)行該動(dòng)畫
public Builder after(Animator anim) 執(zhí)行先執(zhí)行這個(gè)動(dòng)畫再執(zhí)行前面動(dòng)畫
public Builder after(long delay) 延遲n毫秒之后執(zhí)行動(dòng)畫

play(Animator anim)表示當(dāng)前在播放哪個(gè)動(dòng)畫弃秆,另外的with(Animator anim)、before(Animator anim)髓帽、after(Animator anim)都是以play中的當(dāng)前所播放的動(dòng)畫為基準(zhǔn)的菠赚。
當(dāng)play(playAnim)與before(beforeAnim)共用,則表示在播放beforeAnim之前氢卡,先播放playAnim動(dòng)畫锈至;同樣,當(dāng)play(playAnim)與after(afterAnim)共用時(shí)译秦,則表示在在播放afterAnim動(dòng)畫之后峡捡,再播放playAnim動(dòng)畫。

  • 使用示例
/**
     * 按照自定義順序播放動(dòng)畫
     * 首先ivImage09的顏色變化筑悴、位移和ivImage09们拙,同時(shí)發(fā)生
     * 等待前面的動(dòng)畫播放完后 ivImage11,ivImage12才開始動(dòng)畫
     *
     */

 ObjectAnimator objectAnimator01=ObjectAnimator.ofArgb(ivImage09, "BackgroundColor",
                getResources().getColor(R.color.colorPrimary),
                getResources().getColor(R.color.colorAccent),
                getResources().getColor(R.color.colorPrimary));
        ObjectAnimator objectAnimator02 = ObjectAnimator.ofFloat(ivImage09, "TranslationY", 0, 300, 0);
        ObjectAnimator objectAnimator03 = ObjectAnimator.ofFloat(ivImage10, "TranslationY", 0, 400, 0);
        ObjectAnimator objectAnimator04 = ObjectAnimator.ofFloat(ivImage11, "TranslationY", 0, 500, 0);
        ObjectAnimator objectAnimator05 = ObjectAnimator.ofFloat(ivImage12, "TranslationY", 0, 600, 0);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(objectAnimator01).with(objectAnimator02).with(objectAnimator03).before(objectAnimator04).before(objectAnimator05);
        animatorSet.setDuration(2000);
        animatorSet.start();
8-3 PropertyValuesHolder
  • 多屬性動(dòng)畫同時(shí)工作管理類阁吝。有時(shí)候我們需要同時(shí)修改多個(gè)屬性砚婆,那就可以用到此類,具體如下:
PropertyValuesHolder a1 = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);  
PropertyValuesHolder a2 = PropertyValuesHolder.ofFloat("translationY", 0, viewWidth);  
......
ObjectAnimator.ofPropertyValuesHolder(view, a1, a2, ......).setDuration(1000).start();

九突勇、ViewPropertyAnimator

9-1Google官方在Android 3.1系統(tǒng)中補(bǔ)充了ViewPropertyAnimator類装盯,這個(gè)類便是專門為View動(dòng)畫而設(shè)計(jì)的。當(dāng)然這個(gè)類不僅僅是為提供View而簡(jiǎn)單設(shè)計(jì)的甲馋,它存在以下特點(diǎn):

1埂奈、專門針對(duì)View對(duì)象動(dòng)畫而操作的類。
2定躏、提供了更簡(jiǎn)潔的鏈?zhǔn)秸{(diào)用設(shè)置多個(gè)屬性動(dòng)畫账磺,這些動(dòng)畫可以同時(shí)進(jìn)行的。
3痊远、擁有更好的性能垮抗,多個(gè)屬性動(dòng)畫是一次同時(shí)變化,只執(zhí)行一次UI刷新(也就是只調(diào)用一次invalidate,而n個(gè)ObjectAnimator就會(huì)進(jìn)行n次屬性變化碧聪,就有n次invalidate)冒版。
4、每個(gè)屬性提供兩種類型方法設(shè)置scaleX()/scaleXBy()逞姿。
5壤玫、該類只能通過View的animate()獲取其實(shí)例對(duì)象的引用

9-2 每個(gè)屬性豁护,兩種類型方法設(shè)置:

rotationX(20) 改變到某個(gè)值。 旋轉(zhuǎn)到20度欲间。再調(diào)用一次的話楚里,由于已經(jīng)到20度的位置,便不在有變化猎贴。
rotationXBy(20) 改變某個(gè)值的量班缎。 旋轉(zhuǎn)20度。再調(diào)用一次的話她渴,繼續(xù)旋轉(zhuǎn)20度达址,到40度的位置。

9-3常見設(shè)置
view.animate()//獲取ViewPropertyAnimator對(duì)象
                        //動(dòng)畫持續(xù)時(shí)間
                        .setDuration(5000)

                        //透明度
                        .alpha(0)
                        .alphaBy(0)

                        //旋轉(zhuǎn)
                        .rotation(360)
                        .rotationBy(360)
                        .rotationX(360)
                        .rotationXBy(360)
                        .rotationY(360)
                        .rotationYBy(360)

                        //縮放
                        .scaleX(1)
                        .scaleXBy(1)
                        .scaleY(1)
                        .scaleYBy(1)

                        //平移
                        .translationX(100)
                        .translationXBy(100)
                        .translationY(100)
                        .translationYBy(100)
                        .translationZ(100)
                        .translationZBy(100)

                        //更改在屏幕上的坐標(biāo)
                        .x(10)
                        .xBy(10)
                        .y(10)
                        .yBy(10)
                        .z(10)
                        .zBy(10)

                        //插值器
                        .setInterpolator(new BounceInterpolator())//回彈
                        .setInterpolator(new AccelerateDecelerateInterpolator())//加速再減速
                        .setInterpolator(new AccelerateInterpolator())//加速
                        .setInterpolator(new DecelerateInterpolator())//減速
                        .setInterpolator(new LinearInterpolator())//線性

                        //動(dòng)畫延遲
                        .setStartDelay(1000)

                        //是否開啟硬件加速
                        .withLayer()

                        //監(jiān)聽
                        .setListener(new Animator.AnimatorListener() {
                            @Override
                            public void onAnimationStart(Animator animation) {
                                Log.i("MainActivity", "run: onAnimationStart");
                            }

                            @Override
                            public void onAnimationEnd(Animator animation) {
                                Log.i("MainActivity", "run: onAnimationEnd");
                            }

                            @Override
                            public void onAnimationCancel(Animator animation) {
                                Log.i("MainActivity", "run: onAnimationCancel");
                            }

                            @Override
                            public void onAnimationRepeat(Animator animation) {
                                Log.i("MainActivity", "run: onAnimationRepeat");
                            }
                        })

                        .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                            @Override
                            public void onAnimationUpdate(ValueAnimator animation) {
                                Log.i("MainActivity", "run: onAnimationUpdate==");
                            }
                        })

                        .withEndAction(new Runnable() {
                            @Override
                            public void run() {
                                Log.i("MainActivity", "run: end");
                            }
                        })
                        .withStartAction(new Runnable() {
                            @Override
                            public void run() {
                                Log.i("MainActivity", "run: start");
                            }
                        })

                        .start();

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末趁耗,一起剝皮案震驚了整個(gè)濱河市沉唠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌苛败,老刑警劉巖满葛,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異罢屈,居然都是意外死亡嘀韧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門缠捌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锄贷,“玉大人,你說我怎么就攤上這事曼月∫耆矗” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵哑芹,是天一觀的道長(zhǎng)因惭。 經(jīng)常有香客問我,道長(zhǎng)绩衷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任激率,我火速辦了婚禮咳燕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘乒躺。我一直安慰自己招盲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布嘉冒。 她就那樣靜靜地躺著曹货,像睡著了一般咆繁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上顶籽,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天玩般,我揣著相機(jī)與錄音,去河邊找鬼礼饱。 笑死坏为,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的镊绪。 我是一名探鬼主播匀伏,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蝴韭!你這毒婦竟也來了够颠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤榄鉴,失蹤者是張志新(化名)和其女友劉穎履磨,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牢硅,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹬耘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了减余。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片综苔。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖位岔,靈堂內(nèi)的尸體忽然破棺而出如筛,到底是詐尸還是另有隱情,我是刑警寧澤抒抬,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布杨刨,位于F島的核電站,受9級(jí)特大地震影響擦剑,放射性物質(zhì)發(fā)生泄漏妖胀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一惠勒、第九天 我趴在偏房一處隱蔽的房頂上張望赚抡。 院中可真熱鬧,春花似錦纠屋、人聲如沸涂臣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)赁遗。三九已至署辉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間岩四,已是汗流浹背哭尝。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炫乓,地道東北人刚夺。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像末捣,于是被迫代替她去往敵國(guó)和親侠姑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容