• 30、Android属性动画


    属性动画

    由于基本的动画存在局限性,它只改变显示,并不能响应事件,所以在Android 3.0之后,加入了属性动画。

    ObjectAnimation

    创建ObjectAnimation只需要通过静态工厂类直接返回一个ObjectAnimation对象。参数必须包括一个对象和对象的属性名字,属性必须具备get和set方法。

    内部会通过反射机制来调用set方法修改对象的属性值。同样,我们也可以通过setInterpolator设置相应插值器。

    /*
     * Object target,           需要操作的View
     * String propertyName,     需要操作的属性
     * float... values          可变参数,需要传递进去该属性变化的取值过程。
     */
    ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "translationX", 300);
    animator.setDuration(1000);
    animator.start();
    

    在使用ObjectAnimation时,要操纵的属性必须具备get和set方法,不然则会无效。下面是常见的属性值:

    属性 说明
    tanslationX和tanslationY 作为一种增量来控制着View对象从它布局容器的左上角坐标偏移的位置。
    rotation、rotationX和rotationY 控制着View对象围绕指点进行2D和3D旋转。
    scaleX和scaleY 控制着View对象围绕它的支点进行2D缩放。
    pivotX和pivotY 控制着View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。
    x 和 y 描述View对象在它的容器中的最终位置,它是左上角坐标和tanslationX和translationY值的累积和。
    alpha 它表示View对象的alpha透明度,默认值是1(不透明),0代表完全透明。

    如果一个属性没有get和set方法,我们则有两种方案来解决该问题,自定义一个属性类或包装类来间接地给这个属性增加get和set方法。

    public class WrapperView {
        private View mTarget;
        
        public WrapperView(View target){
            mTarget = target;
        }
        
        public int getWidth(){
            return mTarget.getLayoutParams().width;
        }
        
        public void setWidth(int width){
            mTarget.getLayoutParams().width = width;
            mTarget.requestLayout();
        }
    }  
    

    通过以上代码就给属性包装一层,并给它提供get、set方法。使用时只需要操纵包装类就可以间接调用get、set方法了。

    WrapperView wrapper = new WrapperView(mButton);
    ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();  
    

    属性动画拓展

    两种动画集

    • PropertyValuesHolder

      类似基本动画中的AnimationSet,在属性动画中,如果针对同一个对象的多个属性,要同时作用多种动画,可以使用PropertyValuesHolder来实现。

    PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 300f);
    PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
    PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
    ObjectAnimator.ofPropertyValuesHolder(mButton, pvh1, pvh2, pvh3).setDuration(1000).start();  
    
    • AnimatorSet

      对于一个属性同时作用多个属性的动画效果,除了PropertyValuesHolder外,还可以使用AnimatorSet更精准的控制顺序。

    ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 300f);
    ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f, 1f);
    ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f, 1f);
    AnimatorSet set = new AnimatorSet();
    set.setDuration(1000);
    set.playTogether(animator1, animator2, animator3);  
    

    在属性动画中AnimatorSet正是通过playTogether()、playSwquentinally()、animSet.play.with()、befor()、after、这些方法来控制

    多个动画的协同工作并对动画播放顺序进行精准控制。

    ValueAnimator

    ValueAnimator在属性动画中是非常重要的,它是整个属性动画的核心所在,ObjectAnimator继承自ValueAnimator。ValueAnimator本身不提供任何动画效果,

    它更像一个数值发生器,用来产生具有一定规律的数字,从而让调用者来控制动画实现过程。通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值

    的变换,从而完成动画的变换。

    ValueAnimator animator = ValueAnimator.ofFloat(0,100);
    animator.setTarget(mButton);
    animator.setDuration(1000).start();
    animator.addUpdateListener(new AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value = (Float) animation.getAnimatedValue();
            //TODO use the value
        }
    });  
    

    动画的监听

    一个完整的动画具有Start、Repeat、End、Cancel四个过程,通过Android提供了接口,可以方便地监听到这四个事件:

    ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "alpha", 0.5f);
    animator.addListener(new AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {
            
        }  
        @Override
        public void onAnimationRepeat(Animator animation) {
            
        }
        @Override
        public void onAnimationEnd(Animator animation) {
            
        }
        @Override
        public void onAnimationCancel(Animator animation) {
            
        }
    });
    animator.start();  
    

    当然,大部分时候我们只关心onAnimationEnd事件,所以Android也提供了AnimatorListenerAdapter来让我们选择必要的事件进行监听

    animator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            
        }
    });  
    

    XML属性动画

    属性动画同补间动画一样,也可以定义在XML中,代码如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" 
        android:duration="1000"
        android:propertyName="scaleX"
        android:valueFrom="1.0"
        android:valueTo="2.0"
        android:valueType="floatType">
    </objectAnimator>  
    

    在程序中使用XML定义的属性动画也非常的简单:

    public void scaleX(View view){
        Animator anim = AnimatorInflater.loadAnimator(this, R.anim.objectscalex);
        anim.setTarget(view);
        anim.start();
    }  
    

    简写方式

    在Android4.1.2(API 16)时,Google给View增加了animate()方法来直接驱动属性动画,它是属性动画一种简写形式:

    view.animate().alpha(0).y(300).setDuration(3000)
    .withStartAction(new Runnable() {
        @Override
        public void run() {
        }
    }).withEndAction(new Runnable() {
        @Override
        public void run() {
            runOnUiThread(new Runnable() {
                public void run() {
                }
            });
        }
    }).start();  
    

    插值器

    插值器(Interpolators)是动画中一个非常重要的概念,可以自定义动画变换速率,类似物理中的加速度。其作用主要是控制目标变量的变化值进行对应的变化。

    Interpolator用法

    补间器,它的主要作用是可以控制动画的变化速率,比如去实现一种非线性运动的动画效果。补间动画就支持Interpolator,属性动画新增TimeInterpolator接口。
    TimeInterpolator的实现类如下表所示:

    属性 说明
    AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速。
    AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速。
    AnticipateInterpolator 开始的时候向后,然后向前甩。
    AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值。
    BounceInterpolator 动画结束的时候弹起。
    CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线。
    DecelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始减速。
    LinearInterpolator 在动画开始后以均匀的速率改变。
    OvershootInterpolator 向前甩一定值后再回到原来位置。

    使用属性动画时,系统默认的Interpolator其实就是一个先加速后减速的Interpolator,对应的实现类就是AccelerateDecelerateInterpolar。
    我们也可以修改这一默认属性,将它替换成任意一个系统内置好的Interpolator。

    private void startAnimation() {
        Point startPoint = new Point(getWidth() / 2, RADIUS);
        Point endPoint = new Point(getHeight() / 2, getHeight() - RADIUS);
        ValueAnimator animator = ValueAnimator.ofObject(new PointFEvaluator(), startPoint, endPoint);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Point currentPoint = animation.getAnimatedValue();
                invalidate();
            }
        });
        // 使用系统Interpolator实现碰撞反弹效果
        animator.setInterpolator(new BounceInterpolator());
        animator.setDuration(2 * 1000);
        animator.start();
    }
    

    Interpolator详解

    TimeInterpolator

    首先看下TimeInterpolator的接口定义:

    /**
     * A time interpolator defines the rate of change of an animation. This allows animations
     * to have non-linear motion, such as acceleration and deceleration.
     */
    public interface TimeInterpolator {
        /**
         * Maps a value representing the elapsed fraction of an animation to a value that represents
         * the interpolated fraction. This interpolated value is then multiplied by the change in
         * value of an animation to derive the animated value at the current elapsed animation time.
         *
         * @param input A value between 0 and 1.0 indicating our current point
         *        in the animation where 0 represents the start and 1.0 represents
         *        the end
         * @return The interpolation value. This value can be more than 1.0 for
         *         interpolators which overshoot their targets, or less than 0 for
         *         interpolators that undershoot their targets.
         */
        float getInterpolation(float input);
    }
    

    接口非常简单,getInterpolation()方法接收一个input参数,该参数会随着动画的运行而不断有规律的变化,且变化范围是0到1。

    LinearInterpolator
    系统中内置的LinearInterpolator就是一种匀速运动的Interpolator,那么我们来看一下它的源码是怎么实现的

    /**
     * An interpolator where the rate of change is constant
     */
    @HasNativeInterpolator
    public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
        public LinearInterpolator() {
        }
        public LinearInterpolator(Context context, AttributeSet attrs) {
        }
        public float getInterpolation(float input) {
            return input;
        }
        /** @hide */
        @Override
        public long createNativeInterpolator() {
            return NativeInterpolatorFactoryHelper.createLinearInterpolator();
        }
    }
    

    该方法没有任何逻辑,就是把参数中传递的input值直接返回了,因此fraction的值就是等于input的值的,这就是匀速运动的Interpolator的实现方式。

    • AccelerateDecelerateInterpolator

    系统默认情况下使用的是AccelerateDecelerateInterpolator,那么我们看下它的源码:

    /**
     * An interpolator where the rate of change starts and ends slowly but
     * accelerates through the middle.
     */
    @HasNativeInterpolator
    public class AccelerateDecelerateInterpolator extends BaseInterpolator
            implements NativeInterpolatorFactory {
        public AccelerateDecelerateInterpolator() {
        }
        @SuppressWarnings({"UnusedDeclaration"})
        public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
        }
        public float getInterpolation(float input) {
            return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
        }
        /** @hide */
        @Override
        public long createNativeInterpolator() {
            return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
        }
    }
    

    此时getInputerpolator逻辑明显复杂了,不再是直接将参数的input返回,二手进行较为复杂的数学运算。

    自定义Interpolator

    我们已经了解LinearInterpolator和AccelerateDecelerateInterpolator的实现机制,现在我们自定义Interpolator,由于属性动画默认的Interpolator是先减速后加速的过程,

    我们可以修改它为先加速后减速的过程

    public class CustomTimeInterpolator implements TimeInterpolator {
        @Override
        public float getInterpolation(float input) {
            float result;
            if (input <= 0.5) {
                result = (float) (Math.sin(Math.PI * input) / 2);
            } else {
                result = (float) ((2 - Math.sin(Math.PI * input)) / 2)
            }
            return result;
        }
    }
    

    之后我们将自定义的DecelerateAccelerateInterpolator在代码中进行替换:

    private void startAnimation() {
        Point startPoint = new Point(getWidth() / 2, RADIUS);
        Point endPoint = new Point(getHeight() / 2, getHeight() - RADIUS);
        ValueAnimator animator = ValueAnimator.ofObject(new PointFEvaluator(), startPoint, endPoint);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Point currentPoint = animation.getAnimatedValue();
                invalidate();
            }
        });
        // 使用自定义的Interpolator
        animator.setInterpolator(new CustomTimeInterpolator());
        animator.setDuration(2 * 1000);
        animator.start();
    }
    

    自定义动画

    创建自定义动画只需要继承Animation类,并实现applyTransformation方法即可,通常情况下还需要覆盖父类的initalize方法以完成一些初始化操作。

    public class MyAnimation extends Animation {    
        /**完成一些初始化操作*/
        @Override
        public void initialize(int width, int height, int parentWidth,
                int parentHeight) {
            super.initialize(width, height, parentWidth, parentHeight);
        }
        
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
        }
    }  
    

    applyTransformation类的interpolatedTime是插值器因子,由动画当前完成的百分比和当前时间所对应的插值所计算得来的,取值范围是0 - 1.0。

    第二个参数Transformation是矩阵的封装类,一般使用这个类获得当前的矩阵对象

    Matrix matrix = t.getMatrix();  
    

    通过改变matrix对象,可以将动画效果实现出来,基本可以实现任意效果。

    public class CustomAnimation extends Animation {   
        private int mCenterWidth;
        private int mCenterHeight;
        private float mRotateY = 0.0f;
        private Camera mCamera = new Camera();
        /**完成一些初始化操作*/
        @Override
        public void initialize(int width, int height, int parentWidth, int parentHeight) {
            super.initialize(width, height, parentWidth, parentHeight);
            // 设置默认时长
            setDuration(2000);
            // 动画结束后保留状态
            setFillAfter(true);
            // 设置默认插值器
            setInterpolator(new BounceInterpolator());
            mCenterWidth = width / 2;
            mCenterHeight = height / 2;
        }
        // 暴漏接口 -设置旋转角度
        public void setRotateY(float rotateY){
            mRotateY = rotateY;
        }   
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            Matrix matrix = t.getMatrix();
            mCamera.save();
            // 使用Camera设置旋转的角度
            mCamera.rotateY(mRotateY * interpolatedTime);
            // 将旋转变换作用到matrix上
            mCamera.getMatrix(matrix);
            mCamera.restore();
            // 通过pre方法设置矩阵作用前的偏移量来改变旋转中心
            matrix.preTranslate(mCenterWidth, mCenterHeight);
            matrix.preTranslate(-mCenterWidth, -mCenterHeight);
        }
    }  
    

    调用时需要通过对外暴漏的方法来进行设置:

    CustomAnimation customAnimation = new CustomAnimation();
    customAnimation.setRotateY(30);
    view.startAnimation(customAnimation);  
    
  • 相关阅读:
    centos 6.5 下安装RabbitMQ-3.7.28 二进制版本
    Centos 6.5 Rabbitmq 安装和集群,镜像部署
    Vim 自动添加脚本头部信息
    vim 手动添加脚本头部信息
    Pandas系列教程(11)Pandas的索引index
    Pandas系列教程(10)Pandas的axis参数
    Pandas系列教程(9)Pandas字符串处理
    Pandas系列教程(8)pandas数据排序
    Pandas系列教程(7)Pandas的SettingWithCopyWarning
    Pandas系列教程(6)Pandas缺失值处理
  • 原文地址:https://www.cnblogs.com/pengjingya/p/14952683.html
Copyright © 2020-2023  润新知