Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 動畫之屬性動畫ValueAnimator和ObjectAnimator

Android 動畫之屬性動畫ValueAnimator和ObjectAnimator

編輯:關於Android編程

簡介

屬性動畫是API 11新加入的特性,和View動畫不同,它對作用對象進行了擴展,屬性動畫可以對任意對象做動畫,也不像View動畫只支持四種簡單的變化。

屬性動畫的默認時間是300ms,默認頻率是10ms/幀

ObjectAniamtor

先看看一個簡單例子的實現效果,使用ObjectAnimator,它使用起來比較簡單

\

布局就是一個ImageView,看看實現代碼

 

 //透明度變化動畫
    public void alpha(View view) {
//        1、通過調用ofFloat()方法創建ObjectAnimator對象,並設置目標對象、需要改變的目標屬性名、初始值和結束值;
        ObjectAnimator mAnimatorAlpha = ObjectAnimator.ofFloat(mImageView, "alpha", 1.0f, 0.0f);
        //2、設置動畫的持續時間、是否重復及重復次數屬性;
        mAnimatorAlpha.setRepeatMode(Animation.REVERSE);
        mAnimatorAlpha.setRepeatCount(1);
        mAnimatorAlpha.setDuration(1000);
        //3、啟動動畫
        mAnimatorAlpha.start();
    }
    
    //翻轉動畫,翻轉360度
    public void flip(View view) {
        ObjectAnimator visibleToInVisable = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0f, 360.0f);
        //設置插值器
        visibleToInVisable.setInterpolator(new LinearInterpolator());
        visibleToInVisable.setDuration(1000);
        visibleToInVisable.start();
    }
    //縮放動畫
    public void scale(View view) {
        Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scale_anim);
        animator.setTarget(mImageView);
        animator.start();
    }
    
    //平移動畫
    public void translate(View view) {
        ObjectAnimator mAnimatorTranslateX = ObjectAnimator.ofFloat(mImageView, "translationX", 0.0f, screenWidth / 2);
        mAnimatorTranslateX.setRepeatMode(Animation.REVERSE);
        mAnimatorTranslateX.setRepeatCount(1);
        mAnimatorTranslateX.setDuration(1000);

        ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(mImageView, "translationY", 0.0f, screenHeight / 2);
        mAnimatorTranslateY.setRepeatMode(Animation.REVERSE);
        mAnimatorTranslateY.setRepeatCount(1);
        mAnimatorTranslateY.setDuration(1000);

        mAnimatorTranslateX.start();
        mAnimatorTranslateY.start();
    }
    //旋轉動畫
    public void rotate(View view) {
        ObjectAnimator mAnimatorRotate = ObjectAnimator.ofFloat(mImageView, "rotation", 0.0f, 360.0f);
        mAnimatorRotate.setRepeatMode(Animation.REVERSE);
        mAnimatorRotate.setRepeatCount(1);
        mAnimatorRotate.setDuration(2000);

        mAnimatorRotate.start();
    }


 

就是根據ObjectAnimator的ofFloat(Object target, String propertyName, float... values)構造得到一個ObjectAnimator,可以看到第一個參數是Object類型的,意味著我們可以傳入自定義的類型,而不局限於補間動畫只能用view來做,擴展性也就更好。第二個參數是動畫名稱,可以傳遞系統定義好的,比如上面的幾個,也可以自己隨便寫,然後在屬性變化的監聽裡改變。第三個參數就是屬性值,如果只有一個的話會默認為結束值。

上面縮放動畫是用xml實現的,需要在res下面建一個animator文件夾,然後創建相應的xml動畫,如下

 



    

 

組合動畫

現在要多個動畫效果一起實現怎麼辦,有下面幾種方式

1.PropertyValuesHolder

 

 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0.0f, 1.0f);
        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.0f);

        ObjectAnimator animator1 = ObjectAnimator.ofPropertyValuesHolder(mImageView, alpha, scaleX);
        animator1.setDuration(1000);
        animator1.start();
2.監聽屬性變化

 

 

 ObjectAnimator animator = ObjectAnimator.ofFloat(mImageView, "lzy", 0.0f, 1.0f);
        animator.setDuration(1000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                mImageView.setAlpha(value);
                mImageView.setScaleX(1 - value * 0.5f);
            }
        });
        animator.start();
設置屬性變化值在0-1之間,然後在onAnimationUpdate中監聽得到,通過這個值調用view自身的方法改變。

 

3.AnimatorSet

 

AnimatorSet mAnimatorSet = new AnimatorSet();

        ObjectAnimator mAnimatorSetRotateX = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0f, 360.0f);
        mAnimatorSetRotateX.setDuration(3000);

        ObjectAnimator mAnimatorSetRotateY = ObjectAnimator.ofFloat(mImageView, "rotationY", 0.0f, 360.0f);
        mAnimatorSetRotateY.setDuration(3000);

        ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(mImageView, "scaleX", 1.0f, 0.2f);
        mAnimatorScaleX.setRepeatCount(1);
        mAnimatorScaleX.setRepeatMode(Animation.REVERSE);
        mAnimatorScaleX.setDuration(1500);

        ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(mImageView, "scaleY", 1.0f, 0.2f);
        mAnimatorScaleY.setRepeatCount(1);
        mAnimatorScaleY.setRepeatMode(Animation.REVERSE);
        mAnimatorScaleY.setDuration(1500);

        ObjectAnimator mAnimatorScaleY2 = ObjectAnimator.ofFloat(mImageView, "scaleY", 1.0f, 0.2f);
        mAnimatorScaleY2.setRepeatCount(1);
        mAnimatorScaleY2.setRepeatMode(Animation.REVERSE);
        mAnimatorScaleY2.setDuration(1500);

        mAnimatorSet.play(mAnimatorSetRotateY)
                .with(mAnimatorScaleX)
                .with(mAnimatorScaleY)
                .before(mAnimatorSetRotateX).before(mAnimatorScaleY2);

        mAnimatorSet.start();
通過animationSet來實現,它提供了一個play()方法,傳入Animator返回一個Builder,這個Builder中有以下四個方法

 

with(Animator anim),表示同時執行

before(Animator anim),表示將現有動畫插入到傳入動畫之前執行,也就是後執行

after(Animator anim),與before相反

after(long delay),表示延遲多久執行

在測試的時候發現不能重復傳入一個動畫,所以又寫了一個mAnimatorScaleY2 。

AnimationSet中還有playTogether(Animator... items)表示同時執行,playSequentially(Animator... items)表示異步執行。

 

下面做一個讓Button增加寬度的動畫效果

屬性動畫的大致原理:屬性動畫要求動畫作用的對象提供該屬性的get和set方法,就像上面的"scaleX"屬性,它具有getScaleX和setScale方法,屬性動畫會根據傳遞的初始值和結束值,去調用set的方法設置當前的值,如果沒有傳遞初始值會調用get方法去獲取初始值。

然後Button中並沒有我們想要的get和set方法,所以針對這種問題官方文檔給出了下面的解決方法

1.如果有權限的話,給對象加上get和set方法

2.用一個類來包裝原始對象,間接為其提供get和set方法

3.采用ValueAnimator,監聽動畫變化過程,自己實現屬性的變化

我們現在以第二種方式來實現以上效果,代碼如下

 

 mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ViewWrapper wrapper = new ViewWrapper(mButton);
                ObjectAnimator.ofInt(wrapper, "width", 1000).setDuration(1000).start();
            }
        });

 

 

 //用此類來封裝View,間接提供get、set方法
    private static class ViewWrapper {
        private View mView;

        public ViewWrapper(View view) {
            mView = view;
        }

        public void setWidth(int width) {
            mView.getLayoutParams().width = width;
            mView.requestLayout();
        }

        public int getWidth() {
            return mView.getLayoutParams().width;
        }
    }
在這個類中提供了width的get和set方法,getWidth()就是獲取View當前的寬度,上面說過在沒有指定初始值時會調用獲得初始值,而setWidth()方法會不斷被調用,直至動畫結束去改變View的寬度。

 

ValueAnimator

ValueAnimator是ObjectAnimator的父類,也就可以理解為ObjectAnimator是一個封裝好的ValueAnimator,使其使用起來更加的簡單。但是作為父類的ValueAnimator也就使用起來更加的靈活多變。ValueAinamtor本身不會作用於任何對象,直接使用不會有動畫效果,可以理解為它對一個值做動畫,我們通過這個不斷變化的值在監聽函數中去修改相應的屬性。

看看ValueAnimator的幾種構造函數:

ofInt(int... values)

ofArgb(int... values)

ofFloat(float... values)

ofPropertyValuesHolder(PropertyValuesHolder... values)

ofObject(TypeEvaluator evaluator, Object... values)

前三種參數都是初始值到結束值的變化范圍,很簡單

ofPropertyValuesHolder代表多種Animator的集合,下面會介紹具體的使用

ofObject很明顯它的參數類型是Object類型,大多是我們自定義的類型,第一個參數TypeEvaluator 是估值器,因為像上面的int之類的系統都有定義相應的估值器所以不需要我們傳入,然後我們自定義的Object並沒有,所以需要自己的來寫。

來看看系統的IntEvaluator

 

public class IntEvaluator implements TypeEvaluator {

   
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}
估值器都需要繼承自TypeEvaluator,實現裡面的evaluate方法,參數分別是完成的百分比,開始值和結束值,文檔解釋也告訴我們返回的值只需要用結束值減去開始值乘以fraction 然後加上開始值就ok,從而實現值的過渡,所以自定義TypeEvaluator也很簡單了。

 

下面看例子,還是先看效果在撸代碼

\

 

 private void marginValueAnimator() {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, width - mImageView.getWidth());
        //監聽變化過程
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //獲取當前值
                int animatedValueMargin = (int) animation.getAnimatedValue();
                ViewGroup.MarginLayoutParams
                        layoutParams = (ViewGroup.MarginLayoutParams) mImageView.getLayoutParams();
                layoutParams.leftMargin = animatedValueMargin;
                mImageView.setLayoutParams(layoutParams);

            }
        });
        valueAnimator.setDuration(1000);
        valueAnimator.setRepeatCount(3);
        valueAnimator.setRepeatMode(ValueAnimator.REVERSE);

        valueAnimator.setTarget(mImageView);
        valueAnimator.start();
    }
這是個簡單的設置view距離左邊的動畫,構造了ValueAnimator後監聽它的更新,在onAnimationUpdate中改變屬性值,通過animation.getAnimatedValue()獲得當前的值,然後用於更新屬性狀態,實現動畫。

 

 

public void scaleValueAnimator() {
        //1.設置目標屬性名及屬性變化的初始值和結束值
        PropertyValuesHolder mPropertyValuesHolderScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.0f);
        PropertyValuesHolder mPropertyValuesHolderScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.0f);
        ValueAnimator mAnimator = ValueAnimator.ofPropertyValuesHolder(mPropertyValuesHolderScaleX, mPropertyValuesHolderScaleY);
        //2.為目標對象的屬性變化設置監聽器
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 3.根據屬性名獲取屬性變化的值分別為ImageView目標對象設置X和Y軸的縮放值
                float animatorValueScaleX = (float) animation.getAnimatedValue("scaleX");
                float animatorValueScaleY = (float) animation.getAnimatedValue("scaleY");
                mImageView.setScaleX(animatorValueScaleX);
                mImageView.setScaleY(animatorValueScaleY);

            }
        });
        //4.為ValueAnimator設置自定義的Interpolator
        mAnimator.setInterpolator(new CustomInterpolator());
        //5.設置動畫的持續時間、是否重復及重復次數等屬性
        mAnimator.setDuration(2000);
        mAnimator.setRepeatCount(3);
        mAnimator.setRepeatMode(ValueAnimator.REVERSE);
        //6.為ValueAnimator設置目標對象並開始執行動畫
        mAnimator.setTarget(mImageView);
        mAnimator.start();
    }
這裡通過PropertyValuesHolder來控制實現兩種屬性的變化,其實這裡x和y兩個方向的縮放值都是1.0f到0.0f,可以用一個ValueAnimator.ofFloat()來完成,這裡主要是為了演示
ofPropertyValuesHolder的使用。

 

首先是要通過PropertyValuesHolder.ofFloat來創建了兩個PropertyValuesHolder對象,其中第一個參數是屬性名,可以任意取,可以看到在onAnimationUpdate中通過這個屬性名來獲取不同PropertyValuesHolder的變化值。然後再通過ValueAnimator.ofPropertyValuesHolder來構造ValueAnimator對象就可以,也很容易看懂。

上面那個類似拋物線的圓是采用ValueAnimator.ofObject來實現的。

1.首先要定義一個Point類,裡面用於存放x、y坐標

 

public class Point {
    private float x;
    private float y;

    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public float getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}
之前說過要使用ValueAnimator.ofObject需要自定義TypeEvaluator,下面看看定義的PointEvaluator,可以看到和IntEvaluator一樣,主要還是計算變化值

 

 

public class PointEvaluator implements TypeEvaluator {

    //TypeEvaluator簡單來說是實現了初始值和結束值的平滑過渡

    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        Point start = (Point) startValue;
        Point end = (Point) endValue;
        //開始值減去結束值乘以fraction再加上開始值就是當前的值
        float x = start.getX() + fraction * (end.getX() - start.getX());
        float y = start.getY() + fraction * (end.getY() - start.getY());
        Point point = new Point(x, y);
        return point;
    }
下面自定義一個CircleView類
public class CircleView extends View {

    private static final float Radius = 40.0f;
    private Point mPoint;
    private Paint mPaint;


    public CircleView(Context context) {
        super(context);
    }

    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
//        mPoint = new Point(50, 50);
    }

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

    @Override
    protected void onDraw(Canvas canvas) {
        if (mPoint == null) {
            mPoint = new Point(50, 50);
            canvas.drawCircle(mPoint.getX(), mPoint.getY(), Radius, mPaint);
            startAnimation();
        } else {
            canvas.drawCircle(mPoint.getX(), mPoint.getY(), Radius, mPaint);
        }
    }

    private void startAnimation() {
        //創建開始和結束點坐標
        Point start = new Point(50, 50);
        Point end = new Point(getWidth(), getHeight());
        ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), start, end);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //把每一幀的值傳給mPoint,繪制界面
                mPoint = (Point) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.setDuration(5000);
        animator.start();
    }
}
這個類也比較簡單,主要就是在onDraw裡面畫一個圓,然後根據Point的變化不斷調用onDraw方法來改變圓的位置,實現動畫,這也就是ValueAnimator.ofObject的用法。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved