自定义view

android 动画系列三 属性动画(PropertyAnima

2018-09-20  本文已影响107人  赫丹

一、概述

二、特点

三、工作原理

从以上的图片原理分析有三个核心类非常重要:AnimatorSet ValueAnimator ObjectAnimator

四、 ValueAnimator

ValueAnimator 是属性动画的根基了,是应用最广广泛的了,尤其是在自定义 view 中,ValueAnimator类中有几个核心的方法

4-1、ValueAnimator ofInt (int... values)
方式一、java代码设置
 //设置动画的初始值和结束值
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 5);
        //设置动画的时间
        valueAnimator.setDuration(500);
        //设置延时播放
        valueAnimator.setStartDelay(500);
        //设置动画播放重复次数,ValueAnimator.INFINITE=无限重复
        valueAnimator.setRepeatCount(0);
        //设置动画播放模式 ValueAnimator.RESTART=正序播放   ValueAnimator.REVERSE=倒叙播放
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        //将改变的值手动付给对象的属性值:通过动画的更新监听器实现
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int animatedValue = (int) animation.getAnimatedValue();
                Log.d("DXDD", animatedValue + "");
            }
        });
        valueAnimator.start();
效果图
微信图片_20180918164044.png

从上面可以看出数值是从0-5的变化,可以看出并不是均匀分布,,这就和插值器有关系了,,看下面源码

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

通过源码看出插值器的速率是始末速率较慢,中间加速,如果需要改变动画速率可以通过改变插值器的方法进行。从上可以看出插值器和估值器的联系。看下图分析


微信图片_20180918180239.png
方式二、xml代码设置
<?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();
效果图同上,下面结合手动赋值给对象属性做个实例看看效果
 //设置动画的初始值和结束值(图片宽度放大一倍)
        ValueAnimator valueAnimator = ValueAnimator.ofInt(img_girl.getLayoutParams().width, img_girl.getLayoutParams().width * 2);
        //设置动画的时间
        valueAnimator.setDuration(1000);
        //设置动画播放重复次数,ValueAnimator.INFINITE=无限重复
        valueAnimator.setRepeatCount(0);
        //设置动画播放模式 ValueAnimator.RESTART=正序播放   ValueAnimator.REVERSE=倒叙播放
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        //设置插值器
        valueAnimator.setInterpolator(new LinearInterpolator());
        //将改变的值手动付给对象的属性值:通过动画的更新监听器实现
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 每次值变化时,将值手动赋值给对象的属性
                img_girl.getLayoutParams().width = (int) animation.getAnimatedValue();
                //刷新视图,即重新绘制,实现动画效果
                img_girl.requestLayout();
            }
        });
        valueAnimator.start();

 //设置动画的初始值和结束值(图片高度放大一倍)
        ValueAnimator valueAnimator = ValueAnimator.ofInt(img_girl.getLayoutParams().height, img_girl.getLayoutParams().height * 2);
        //设置动画的时间
        valueAnimator.setDuration(1000);
        //设置动画播放重复次数,ValueAnimator.INFINITE=无限重复
        valueAnimator.setRepeatCount(2);
        //设置动画播放模式 ValueAnimator.RESTART=正序播放   ValueAnimator.REVERSE=倒叙播放
        valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
        //设置插值器
        valueAnimator.setInterpolator(new LinearInterpolator());
        //将改变的值手动付给对象的属性值:通过动画的更新监听器实现
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 每次值变化时,将值手动赋值给对象的属性
                img_girl.getLayoutParams().height = (int) animation.getAnimatedValue();
                //刷新视图,即重新绘制,实现动画效果
                img_girl.requestLayout();
            }
        });
        valueAnimator.start();
效果图
girl.gif

合并是两种动画同事播放,,感觉代码有点重复使用吧,,学习完下面的内容会有更简单的实现方法

4-2、ValueAnimator ofFloat (float... values)使用方法和效果同上,不同的地方是监听返回值是float类型,返回的类型不同主要是因为使用了不同的估值器(估值器在下面会详细介绍)
4-3、ValueAnimator ofArgb (int... values)

ValueAnimator中的ofArgb()可以帮助我们实现颜色的渐变效果,Google在API LEVEL 21之后增加了这个方法ofArgb()。通过这个方法我们更容易地实现颜色演变,通过ofArgb和ArgbEvaluator,我们可以轻松实现颜色渐变效果

java代码
 //第一种方法
        //设置初始值过度值和结束值
        ValueAnimator argbAnim = ValueAnimator.ofArgb(Color.parseColor("#ff669900"), Color.parseColor("#ff0099cc"), Color.parseColor("#ffff4444"), Color.parseColor("#ff669900"));
       //设置动画时间
        argbAnim.setDuration(2000);
        //设置匀速插值器
        argbAnim.setInterpolator(new LinearInterpolator());
        //动画监听
        argbAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                btn_Argb.setBackgroundColor((Integer) animation.getAnimatedValue());
            }
        });
        argbAnim.start();
//第二种方法
        //设置初始值过度值和结束值
        ValueAnimator argbAnim = ValueAnimator.ofInt(Color.parseColor("#ff669900"), Color.parseColor("#ff0099cc"), Color.parseColor("#ffff4444"), Color.parseColor("#ff669900"));
        //设置动画时间
        argbAnim.setDuration(2000);
        //设置估值器
        argbAnim.setEvaluator(new ArgbEvaluator());
        //设置匀速插值器
        argbAnim.setInterpolator(new LinearInterpolator());
        //动画监听
        argbAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                btn_Argb.setBackgroundColor((Integer) animation.getAnimatedValue());
            }
        });
        argbAnim.start();
效果图如下:(需要向下兼容用第二张方法即可实现)
argb.gif
4-4、ValueAnimator ofObject (TypeEvaluator evaluator, Object... values)

ofObject方法是什么意思呢?我们都知道ofInt和ofFloat都是针对Int值和Float值的变化,但是,我们只能控制一个值的变化,但是当我们需要实现多值变化时,它们就不再满足我们的需求。因为两个属性的初始值和结束值不一样,那么我们就可以将两个属性值封装到一个对象里面,那么初始值的object和结束值的object就可以包含两个属性不同的初始值和结束值了,这个时候就需要自定义估值TypeEvaluator器达到效果。介绍完估值器在进行ValueAnimator ofObject示例练习

五、估值器 (TypeEvaluator)

系统默认提供了四种估值器,如果达不到要求那么就需要我们自定义估值器,需要自定义估值器就需要实现TypeEvaluator接口,复写evaluate()
public interface TypeEvaluator<T> {
    /**
     *
     * @param fraction  插值器getInterpolation()的返回值 
     * @param startValue 起始值.
     * @param endValue   结束值.
     * @return  T   赋给动画属性的具体数值
     *    
     */
    public T evaluate(float fraction, T startValue, T endValue);

前面介绍过插值器与估值器的关系,其实就事插值器中input 和 fraction关系:input的值决定了fraction的值:input值经过计算后传入到插值器的getInterpolation(),然后通过实现getInterpolation()中的逻辑算法,根据input值来计算出一个返回值,而这个返回值就是fraction,那么先来看一个系统的插值器:浮点型插值器:FloatEvaluator

public class FloatEvaluator implements TypeEvaluator<Number> {
    // FloatEvaluator实现了TypeEvaluator接口

    // 重写evaluate()
    public Float evaluate(float fraction, Number startValue, Number endValue) {
     // fraction:表示动画完成度(根据它来计算当前动画的值)
    // startValue、endValue:动画的初始值和结束值
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
      //计算方式:初始值+动画百分比*(结束值-初始值)
    }
效果图
buy.gif

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   開始的数据点
     * @param end     结束的数据点
     * @param control 控制点
     * @param t       float 0-1
     * @return 不同t相应的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;
    }

    private void clickView(View view) {
        int[] clickCoordinate = new int[2];
        int[] parentCoordinate = new int[2];
        int[] buyCoordinate = new int[2];
        //分别获取被点击view、父布局、购物车在屏幕上的坐标
        view.getLocationOnScreen(clickCoordinate);
        shopping_container.getLocationOnScreen(parentCoordinate);
        img_buy.getLocationOnScreen(buyCoordinate);

        //创建滑动的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]);

        //计算滑动imageview的三个控制点(起点、结束点、控制点)
        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;

        //执行动画
        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();
    }
  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();
    }

六、ObjectAnimator

上面说了ObjectAnimator是ValueAnimator的子类,是对ValueAnimator操作的封装,目的就是让我们的属性动画操作更加简单,这符合 java 简单化的思想,大伙仔细想想,简单好用就是我们码代码的终极追求之一啊,下面来看看常用属性和用法:

6-1 常用的propertyName:
6-2 使用方法:
ObjectAnimator animator = ObjectAnimator.ofFloat(testView, "translationX", 100);
animator.setDuration(600);
animator.setRepeatMode(ObjectAnimator.REVERSE);
animator.setRepeatCount(1);
animator.start();

七、path动画

5.0版本属性动画中添加了一个path动画,可以让 view 沿着 path 的路径做动画
  ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, View.X, View.Y, path_view.getPath());
                animator.setDuration(1000);
                animator.setRepeatMode(ObjectAnimator.REVERSE);
                animator.setRepeatCount(1);
                animator.start();

八、AnimatorSet 动画集合

8-1在AnimatorSet中给为我们提供了两个方法playSequentially和playTogether
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(自由设置动画顺序)

AnimatorSet.Builder用于实现playTogether和playSequentially无法实现的效果,可以实现非常自由的组合动画,比如有三个动画A,B,C想先播放C然后同时播放A和B,利用playTogether和playSequentially是没办法实现的,但利用AnimatorSet.Builder却可以轻易实现。

AnimatorSet.Builder常用方法:
方法 说明
public Builder play(Animator anim) 表示要播放哪个动画 AnimatorSet中的play方法是获取AnimatorSet.Builder对象的唯一途径
public Builder with(Animator anim) 和前面动画一起执行
public Builder before(Animator anim) 执行前面的动画后才执行该动画
public Builder after(Animator anim) 执行先执行这个动画再执行前面动画
public Builder after(long delay) 延迟n毫秒之后执行动画

play(Animator anim)表示当前在播放哪个动画,另外的with(Animator anim)、before(Animator anim)、after(Animator anim)都是以play中的当前所播放的动画为基准的。
当play(playAnim)与before(beforeAnim)共用,则表示在播放beforeAnim之前,先播放playAnim动画;同样,当play(playAnim)与after(afterAnim)共用时,则表示在在播放afterAnim动画之后,再播放playAnim动画。

/**
     * 按照自定义顺序播放动画
     * 首先ivImage09的颜色变化、位移和ivImage09,同时发生
     * 等待前面的动画播放完后 ivImage11,ivImage12才开始动画
     *
     */

 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
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系统中补充了ViewPropertyAnimator类,这个类便是专门为View动画而设计的。当然这个类不仅仅是为提供View而简单设计的,它存在以下特点:

1、专门针对View对象动画而操作的类。
2、提供了更简洁的链式调用设置多个属性动画,这些动画可以同时进行的。
3、拥有更好的性能,多个属性动画是一次同时变化,只执行一次UI刷新(也就是只调用一次invalidate,而n个ObjectAnimator就会进行n次属性变化,就有n次invalidate)。
4、每个属性提供两种类型方法设置scaleX()/scaleXBy()。
5、该类只能通过View的animate()获取其实例对象的引用

9-2 每个属性,两种类型方法设置:

rotationX(20) 改变到某个值。 旋转到20度。再调用一次的话,由于已经到20度的位置,便不在有变化。
rotationXBy(20) 改变某个值的量。 旋转20度。再调用一次的话,继续旋转20度,到40度的位置。

9-3常见设置
view.animate()//获取ViewPropertyAnimator对象
                        //动画持续时间
                        .setDuration(5000)

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

                        //旋转
                        .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)

                        //更改在屏幕上的坐标
                        .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())//线性

                        //动画延迟
                        .setStartDelay(1000)

                        //是否开启硬件加速
                        .withLayer()

                        //监听
                        .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();

上一篇 下一篇

猜你喜欢

热点阅读