美文网首页
Android 属性动画

Android 属性动画

作者: gaookey | 来源:发表于2022-02-17 12:02 被阅读0次
    image.png

    属性动画是增强版的补间动画

    • 补间动画只能定义两个关键帧在“透明度”“旋转”“缩放”“位移”4个方面的变化,但属性动画可以定义任何属性的变化。
    • 补间动画只能对 UI组件执行动画,但属性动画几乎可以对任何对象执行动画(不管它是否显示在屏幕上)。

    与补间动画类似的是,属性动画也需要定义如下几个属性。

    • 动画持续时间:该属性的默认值是300ms。在属性动画资源文件中通过 android:duration 属性指定。
    • 动画插值方式:该属性的作用与补间动画中插值属性的作用基本类似。在属性动画资源文件中通过 android:interpolator 属性指定。
    • 动画重复次数:指定动画重复播放的次数。在属性动画资源文件中通过 android:repeatCount属性指定。
    • 重复行为:指定动画播放结束后、重复下次动画时,是从开始帧再次播放到结束帧,还是从结束帧反向播放到开始帧。在属性动画资源文件中通过 android:repeatMode 属性指定。
    • 动画集:开发者可以将多个属性动画合并成一组,既可让这组属性动画按次序播放,也可让这组属性动画同时播放。在属性动画资源文件中通过 <set../> 元素来组合,该元素的 android:ordering 属性指定该组动画是按次序播放,还是同时播放。
    • 帧刷新频率:指定每隔多长时间播放一帧。该属性的默认值为10ms。

    属性动画的 API

    • Animator 它提供了创建属性动画的基类,基本上不会直接使用该类。通常该类只用于被继承并重写它的相关方法。
    • ValueAnimator 属性动画主要的时间引擎,它负责计算各个帧的属性值。它定义了属性动画的绝大部分核心功能,包括计算各帧的相关属性值,负责处理更新事件,按属性值的类型控制计算规则。属性动画主要由两方面组成:①计算各帧的相关属性值;②为指定对象设置这些计算后的值。ValueAnimator 只负责第一方面内容,因此程序员必须根据 ValueAnimator 计算并监听值更新来更新对象的相关属性值。
    • ObjectAnimator 它是 ValueAnimator 的子类,允许程序员对指定对象的属性执行动画。在实际应用中,ObjectAnimator 使用起来更加简单,因此更加常用。在少数场景下,由于ObjectAnimator 存在一些限制,可能需要考虑使用 ValueAnimator
    • AnimatorSet 它是 Animator 的子类,用于组合多个 Animator,并指定多个 Animator 是按次序播放,还是同时播放。

    除此之外,属性动画还需要利用一个 Evaluator(计算器),该工具类控制属性动画如何计算属性值。Android 提供了如下 Evaluator

    • IntEvaluator 用于计算 int 类型属性值的计算器。
    • FloatEvaluator 用于计算 float 类型属性值的计算器。
    • ArgbEvaluator 用于计算以十六进制形式表示的颜色值的计算器。
    • TypeEvaluator 它是计算器接口,开发者可以通过实现该接口来实现自定义计算器。如果需要对 intfoat 或者颜色值以外类型的属性执行属性动画,可能需要实现 TypeEvaluator 接口来实现自定义计算器。

    Android 8 还为 AnimatorSet 新增了如下方法。

    • reverse() 反向播放属性动画。
    • long getCurrentPlayTime() 获取动画的当前播放时间。
    • setCurrentPlayTime(long playTime) 设置动画的播放时间。

    通过调用 setCurrentPlayTime() 方法,Android 允许 AnimatorSet 动画直接调到指定时间点进行播放,不需要总是从头开始播放;通过 reverse() 方法则允许对动画进行倒播--以前可能需要定义两组动画,其中一组用于正向播放(比如放大、淡入);另一组用于反向播放(比如缩小、淡出),有了倒播功能之后,只需定义一组动画即可。

    1. 使用 ValueAnimator 创建动画

    使用 ValueAnimator 创建动画可按如下4个步骤进行。

    1. 调用 ValueAnimatorofint()ofFloat()ofObject() 静态方法创建 ValueAnimator 实例。
    2. 调用 ValueAnimatorsetXxx() 方法设置动画持续时间、插值方式、重复次数等。
    1. 调用 ValucAnimatorstart() 方法启动动画。
    2. ValueAnimator 注册 AnimatorUpdateListener 监听器,在该监听器中可以监听 ValueAnimator 计算出来的值的改变,并将这些值应用到指定对象上。
    ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
    animator.setDuration(1000);
    animator.start();
    

    自定义的 Evaluator 计算器:

    ValueAnimator animation = ValueAnimator.ofObject (MyTypeEvaluator (), startVal, endVal) ;
    animation.setDuration (1000);
    animation. start();
    

    在上面的代码片段中,ValueAnimator 仅仅是计算动画过程中变化的值,并没有把这些计算出来的值应用到任何对象上,因此也不会显示任何动画。
    如果希望使用 ValueAnimator 创建动画,还需要注册一个监听器:AnimatorUpdateListener,该监听器负责更新对象的属性值。在实现这个监听器时,可以通过 getAnimatedValue() 方法来获取当前帧的值,并将该计算出来的值应用到指定对象上。当该对象的属性持续改变时,该对象也就呈现出动画效果了。

    2. 使用 ObjectAnimator 创建动画

    ObjectAnimator 继承了 ValueAnimator,因此它可以直接将 ObjectAnimator 在动画过程中计算出来的值应用到指定对象的指定属性上(ValueAnimator 则需要注册一个监听器来完成这个工作)。因此使用 ObjectAnimator 就不需要注册 AnimatorUpdateListener 监听器了。
    使用 ObjectAnimatorofInt()ofFloat()ofObject() 静态方法创建 ObjectAnimator 时,需要指定具体的对象,以及对象的属性名。

    ObjectAnimator anim = ObjectAnimator.ofFloat (foo, "alpha", 0f, 1f);
    anim.setDuration(1000);
    anim.start();
    

    ValueAnimator 不同的是,使用 ObjectAnimator 有如下几个注意点。

    • 要为该对象对应的属性提供 setter 方法,如上例中需要为 foo 对象提供 setAlpha(float value) 方法。
    • 调用 ObjectAnimatorofInt()、ofFloat()ofObject()工厂方法时,如果values...参数只提供了一个值(本来需要提供开始值和结束值),那么该值会被认为是结束值。该对象应该为该属性提供一个getter方法,该getter` 方法的返回值将被作为开始值。
    • 如果动画的对象不是 View,为了能显示动画效果,可能还需要在 onAnimationUpdate() 事件监听方法中调用 View.invalidate() 方法来刷新屏幕的显示,比如对 Drawable 对象的 color
      属性执行动画。但如果是 View 定义的 setter 方法,如 setAIpha()setTranslationX() 等方法,都会自动地调用 invalidate() 方法,因此不需要额外地调用 invalidate()` 方法。

    使用属性动画

    属性动画既可作用于 UI 组件,也可作用于普通的对象(即使它没有在UI 界面上绘制出来)。

    定义属性动画有如下两种方式。

    1. 使用 ValueAnimatorObjectAnimator 的静态工厂方法来创建动画。
    2. 使用资源文件来定义动画。

    使用属性动画的步骤如下。

    1. 创建 ValueAnimatorObjectAnimator 对象--既可从 XML 资源文件加载该动画资源,也可直接调用 ValueAnimatorObjectAnimator 的静态工厂方法来创建动画。
    2. 根据需要为 Animator 对象设置属性。
    3. 如果需要监听 Animator 的动画开始事件、动画结束事件、动画重复事件、动画值改变事件,并根据事件提供相应的处理代码,则应该为 Animator 对象设置事件监听器。
    4. 如果有多个动画需要按次序或同时播放,则应使用 AnimatorSet 组合这些动画。
    5. 调用 Animator 对象的 start() 方法启动动画。

    MainActivity

    public class MainActivity extends AppCompatActivity {
        // 定义小球大小的常量
        public static final float BALL_SIZE = 50f;
        // 定义小球从屏幕上方下落到屏幕底端的总时间
        public static final float FULL_TIME = 1000f;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            LinearLayout container = findViewById(R.id.container);
            // 设置该窗口显示MyAnimationView组件
            container.addView(new MyAnimationView(this));
        }
    
        static class MyAnimationView extends View implements ValueAnimator.AnimatorUpdateListener {
            List<ShapeHolder> balls = new ArrayList<>();
    
            public MyAnimationView(Context context) {
                super(context);
                setBackgroundColor(Color.WHITE);
            }
    
            @Override
            public boolean onTouchEvent(MotionEvent event) {
                // 如果触碰事件不是按下、移动事件
                if (event.getAction() != MotionEvent.ACTION_DOWN &&
                        event.getAction() != MotionEvent.ACTION_MOVE) {
                    return false;
                }
                // 在事件发生点添加一个小球(用一个圆形代表)
                ShapeHolder newBall = addBall(event.getX(), event.getY());
                // 计算小球下落动画开始时的y坐标
                float startY = newBall.getY();
                // 计算小球下落动画结束时的y坐标(落到屏幕最下方,就是屏幕高度减去小球高度)
                float endY = getHeight() - BALL_SIZE;
                // 获取屏幕高度
                float h = getHeight();
                float eventY = event.getY();
                // 计算动画的持续时间
                int duration = (int) (FULL_TIME * ((h - eventY) / h));
                // 定义小球“落下”的动画:
                // 让newBall对象的y属性从事件发生点变化到屏幕最下方
                ObjectAnimator fallAnim = ObjectAnimator.ofFloat(newBall, "y", startY, endY);
                // 设置fallAnim动画的持续时间
                fallAnim.setDuration(duration);
                // 设置fallAnim动画的插值方式:加速插值
                fallAnim.setInterpolator(new AccelerateInterpolator());
                // 为fallAnim动画添加监听器
                // 当ValueAnimator的属性值发生改变时,将会激发该监听器的事件监听方法
                fallAnim.addUpdateListener(this);
                // 定义对newBall对象的alpha属性执行从1到0的动画(即定义渐隐动画)
                ObjectAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
                // 设置动画持续时间
                fadeAnim.setDuration(250);
                // 为fadeAnim动画添加监听器
                fadeAnim.addListener(new AnimatorListenerAdapter() {
                    // 当动画结束时
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        // 动画结束时将该动画关联的ShapeHolder删除
                        balls.remove(((ObjectAnimator) animation).getTarget());
                    }
                });
                // 为fadeAnim动画添加监听器
                // 当ValueAnimator的属性值发生改变时,将会激发该监听器的事件监听方法
                fadeAnim.addUpdateListener(this);
                // 定义一个AnimatorSet来组合动画
                AnimatorSet animatorSet = new AnimatorSet();
                // 指定在播放fadeAnim之前,先播放fallAnim动画
                animatorSet.play(fallAnim).before(fadeAnim);
                // 开始播放动画
                animatorSet.start();
                return true;
            }
    
            private ShapeHolder addBall(float x, float y) {
                // 创建一个圆
                OvalShape circle = new OvalShape();
                // 设置该圆的宽、高
                circle.resize(BALL_SIZE, BALL_SIZE);
                // 将圆包装成Drawable对象
                ShapeDrawable drawable = new ShapeDrawable(circle);
                // 创建一个ShapeHolder对象
                ShapeHolder shapeHolder = new ShapeHolder(drawable);
                // 设置ShapeHolder的x、y坐标
                shapeHolder.setX(x - BALL_SIZE / 2);
                shapeHolder.setY(y - BALL_SIZE / 2);
                int red = (int) (Math.random() * 255);
                int green = (int) (Math.random() * 255);
                int blue = (int) (Math.random() * 255);
                // 将red、green、blue三个随机数组合成ARGB颜色
                int color = -0x1000000 + red << 16 | (green << 8) | blue;
                // 获取drawable上关联的画笔
                Paint paint = drawable.getPaint();
                // 将red、green、blue三个随机数除以4得到商值组合成ARGB颜色
                int darkColor = (-0x1000000 | (red / 4 << 16) | (green / 4 << 8) | blue / 4);
                // 创建圆形渐变
                RadialGradient gradient = new RadialGradient(37.5f, 12.5f, BALL_SIZE,
                        color, darkColor, Shader.TileMode.CLAMP);
                paint.setShader(gradient);
                // 为shapeHolder设置paint画笔
                shapeHolder.setPaint(paint);
                balls.add(shapeHolder);
                return shapeHolder;
            }
    
            @Override
            public void onDraw(Canvas canvas) {
                // 遍历balls集合中的每个ShapeHolder对象
                for (ShapeHolder shapeHolder : balls) {
                    // 保存canvas的当前坐标系统
                    canvas.save();
                    // 坐标变换:将画布坐标系统平移到shapeHolder的X、Y坐标处
                    canvas.translate(shapeHolder.getX(), shapeHolder.getY());
                    // 将shapeHolder持有的圆形绘制在Canvas上
                    shapeHolder.getShape().draw(canvas);
                    // 恢复Canvas坐标系统
                    canvas.restore();
                }
            }
    
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 指定重绘该界面
                this.invalidate();  // ①
            }
        }
    }
    

    ShapeHolder

    public class ShapeHolder {
        private float x = 0, y = 0;
        private ShapeDrawable shape;
        private int color;
        private RadialGradient gradient;
        private float alpha = 1f;
        private Paint paint;
    
        public ShapeHolder(ShapeDrawable s) {
            shape = s;
        }
    
        public float getX() {
            return x;
        }
    
        public void setX(float x) {
            this.x = x;
        }
    
        public float getY() {
            return y;
        }
    
        public void setY(float y) {
            this.y = y;
        }
    
        public ShapeDrawable getShape() {
            return shape;
        }
    
        public void setShape(ShapeDrawable shape) {
            this.shape = shape;
        }
    
        public int getColor() {
            return color;
        }
    
        public void setColor(int color) {
            this.color = color;
        }
    
        public RadialGradient getGradient() {
            return gradient;
        }
    
        public void setGradient(RadialGradient gradient) {
            this.gradient = gradient;
        }
    
        public float getAlpha() {
            return alpha;
        }
    
        public void setAlpha(float alpha) {
            this.alpha = alpha;
        }
    
        public Paint getPaint() {
            return paint;
        }
    
        public void setPaint(Paint paint) {
            this.paint = paint;
        }
    }
    
    image.gif

    实例:大珠小珠落玉盘

    MainActivity

    public class MainActivity extends AppCompatActivity {
        // 定义小球大小的常量
        public static final float BALL_SIZE = 50f;
        // 定义小球从屏幕上方下落到屏幕底端的总时间
        public static final float FULL_TIME = 1000f;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            LinearLayout container = findViewById(R.id.container);
            // 设置该窗口显示MyAnimationView组件
            container.addView(new MyAnimationView(this));
    
        }
    
        class MyAnimationView extends View {
            List<ShapeHolder> balls = new ArrayList<>();
    
            MyAnimationView(Context context) {
                super(context);
                // 加载动画资源
                ObjectAnimator colorAnim = (ObjectAnimator) AnimatorInflater.loadAnimator(MainActivity.this,
                        R.animator.color_anim);
                colorAnim.setEvaluator(new ArgbEvaluator());
                // 对该View本身应用属性动画
                colorAnim.setTarget(this);
                // 开始指定动画
                colorAnim.start();
            }
    
            @Override
            public boolean onTouchEvent(MotionEvent event) {
                // 如果触碰事件不是按下、移动事件
                if (event.getAction() != MotionEvent.ACTION_DOWN &&
                        event.getAction() != MotionEvent.ACTION_MOVE) {
                    return false;
                }
                // 在事件发生点添加一个小球(用一个圆形代表)
                ShapeHolder newBall = addBall(event.getX(), event.getY());
                // 计算小球下落动画开始时的y坐标
                float startY = newBall.getY();
                // 计算小球下落动画结束时的y坐标(落到屏幕最下方,就是屏幕高度减去小球高度)
                float endY = getHeight() - BALL_SIZE;
                // 获取屏幕高度
                float h = getHeight();
                float eventY = event.getY();
                // 计算动画的持续时间
                int duration = (int) (FULL_TIME * ((h - eventY) / h));
                // 定义小球“落下”的动画
                // 让newBall对象的y属性从事件发生点变化到屏幕最下方
                ObjectAnimator fallAnim = ObjectAnimator.ofFloat(newBall, "y", startY, endY);
                // 设置fallAnim动画的持续时间
                fallAnim.setDuration(duration);
                // 设置fallAnim动画的插值方式:加速插值
                fallAnim.setInterpolator(new AccelerateInterpolator());
                // 定义小球“压扁”的动画:该动画控制小球的x坐标“左移”半个球
                ObjectAnimator squashAnim1 = ObjectAnimator.ofFloat(newBall,
                        "x", newBall.getX(), newBall.getX() - BALL_SIZE / 2);
                // 设置squashAnim1动画的持续时间
                squashAnim1.setDuration(duration / 4);
                // 设置squashAnim1动画重复1次
                squashAnim1.setRepeatCount(1);
                // 设置squashAnim1动画的重复方式
                squashAnim1.setRepeatMode(ValueAnimator.REVERSE);
                // 设置squashAnim1动画的插值方式:减速插值
                squashAnim1.setInterpolator(new DecelerateInterpolator());
                // 定义小球“压扁”的动画:该动画控制小球的宽度加倍
                ObjectAnimator squashAnim2 = ObjectAnimator.ofFloat(newBall, "width",
                        newBall.getWidth(), newBall.getWidth() + BALL_SIZE);
                // 设置squashAnim2动画的持续时间
                squashAnim2.setDuration(duration / 4);
                // 设置squashAnim2动画重复1次
                squashAnim2.setRepeatCount(1);
                // 设置squashAnim2动画的重复方式
                squashAnim2.setRepeatMode(ValueAnimator.REVERSE);
                // 设置squashAnim2动画的插值方式:减速插值
                squashAnim2.setInterpolator(new DecelerateInterpolator());
                // 定义小球“拉伸”的动画:该动画控制小球的y坐标“下移”半个球
                ObjectAnimator stretchAnim1 = ObjectAnimator.ofFloat(newBall,
                        "y", endY, endY + BALL_SIZE / 2);
                // 设置stretchAnim1动画的持续时间
                stretchAnim1.setDuration(duration / 4);
                // 设置stretchAnim1动画重复1次
                stretchAnim1.setRepeatCount(1);
                // 设置stretchAnim1动画的重复方式
                stretchAnim1.setRepeatMode(ValueAnimator.REVERSE);
                // 设置stretchAnim1动画的插值方式:减速插值
                stretchAnim1.setInterpolator(new DecelerateInterpolator());
                // 定义小球“拉伸”的动画:该动画控制小球的高度减半
                ObjectAnimator stretchAnim2 = ObjectAnimator.ofFloat(newBall, "height",
                        newBall.getHeight(), newBall.getHeight() - BALL_SIZE / 2);
                // 设置stretchAnim2动画的持续时间
                stretchAnim2.setDuration(duration / 4);
                // 设置squashAnim2动画重复1次
                stretchAnim2.setRepeatCount(1);
                // 设置squashAnim2动画的重复方式
                stretchAnim2.setRepeatMode(ValueAnimator.REVERSE);
                // 设置squashAnim2动画的插值方式:减速插值
                stretchAnim2.setInterpolator(new DecelerateInterpolator());
                // 定义小球“弹起”的动画
                ObjectAnimator bounceBackAnim = ObjectAnimator.ofFloat(newBall, "y", endY, startY);
                // 设置持续时间
                bounceBackAnim.setDuration(duration);
                // 设置动画的插值方式:减速插值
                bounceBackAnim.setInterpolator(new DecelerateInterpolator());
                // 使用AnimatorSet按顺序播放“下落/压扁&拉伸/弹起动画
                AnimatorSet bouncer = new AnimatorSet();
                // 定义在squashAnim1动画之前播放fallAnim下落动画
                bouncer.play(fallAnim).before(squashAnim1);
                // 由于小球在“屏幕”下方弹起时,小球要被压扁
                // 即:宽度加倍、x坐标左移半个球,高度减半、y坐标下移半个球
                // 因此此处指定播放squashAnim1的同时
                // 还播放squashAnim2、stretchAnim1、stretchAnim2
                bouncer.play(squashAnim1).with(squashAnim2);
                bouncer.play(squashAnim1).with(stretchAnim1);
                bouncer.play(squashAnim1).with(stretchAnim2);
                // 指定播放stretchAnim2动画之后,播放bounceBackAnim弹起动画
                bouncer.play(bounceBackAnim).after(stretchAnim2);
                // 定义对newBall对象的alpha属性执行从1到0的动画(即定义渐隐动画)
                ObjectAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
                // 设置动画持续时间
                fadeAnim.setDuration(250);
                // 为fadeAnim动画添加监听器
                fadeAnim.addListener(new AnimatorListenerAdapter() {
                    // 当动画结束时
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        // 动画结束时将该动画关联的ShapeHolder删除
                        balls.remove(((ObjectAnimator) animation).getTarget());
                    }
                });
                // 再次定义一个AnimatorSet来组合动画
                AnimatorSet animatorSet = new AnimatorSet();
                // 指定在播放fadeAnim之前,先播放bouncer动画
                animatorSet.play(bouncer).before(fadeAnim);
                // 开始播放动画
                animatorSet.start();
                return true;
            }
    
            private ShapeHolder addBall(float x, float y) {
                // 创建一个椭圆
                OvalShape circle = new OvalShape();
                // 设置该椭圆的宽、高
                circle.resize(BALL_SIZE, BALL_SIZE);
                // 将椭圆包装成Drawable对象
                ShapeDrawable drawable = new ShapeDrawable(circle);
                // 创建一个ShapeHolder对象
                ShapeHolder shapeHolder = new ShapeHolder(drawable);
                // 设置ShapeHolder的x、y坐标
                shapeHolder.setX(x - BALL_SIZE / 2);
                shapeHolder.setY(y - BALL_SIZE / 2);
                int red = (int) (Math.random() * 255);
                int green = (int) (Math.random() * 255);
                int blue = (int) (Math.random() * 255);
                // 将red、green、blue三个随机数组合成ARGB颜色
                int color = -0x1000000 + red << 16 | (green << 8) | blue;
                // 获取drawable上关联的画笔
                Paint paint = drawable.getPaint();
                // 将red、green、blue三个随机数除以4得到商值组合成ARGB颜色
                int darkColor = (-0x1000000 | (red / 4 << 16) | (green / 4 << 8) | blue / 4);
                // 创建圆形渐变
                RadialGradient gradient = new RadialGradient(37.5f, 12.5f, BALL_SIZE,
                        color, darkColor, Shader.TileMode.CLAMP);
                paint.setShader(gradient);
                // 为shapeHolder设置paint画笔
                shapeHolder.setPaint(paint);
                balls.add(shapeHolder);
                return shapeHolder;
            }
    
            @Override
            public void onDraw(Canvas canvas) {
                // 遍历balls集合中的每个ShapeHolder对象
                for (ShapeHolder shapeHolder : balls) {
                    // 保存canvas的当前坐标系统
                    canvas.save();
                    // 坐标变换:将画布坐标系统平移到shapeHolder的X、Y坐标处
                    canvas.translate(shapeHolder.getX(), shapeHolder.getY());
                    // 将shapeHolder持有的圆形绘制在Canvas上
                    shapeHolder.getShape().draw(canvas);
                    // 恢复Canvas坐标系统
                    canvas.restore();
                }
            }
        }
    }
    

    ShapeHolder

    public class ShapeHolder {
        private float x = 0, y = 0;
        private ShapeDrawable shape;
        private int color;
        private RadialGradient gradient;
        private float alpha = 1f;
        private Paint paint;
    
        public ShapeHolder(ShapeDrawable s) {
            shape = s;
        }
    
        public float getWidth() {
            return shape.getShape().getWidth();
        }
    
        public void setWidth(float width) {
            Shape s = shape.getShape();
            s.resize(width, s.getHeight());
        }
    
        public float getHeight() {
            return shape.getShape().getHeight();
        }
    
        public void setHeight(float height) {
            Shape s = shape.getShape();
            s.resize(s.getWidth(), height);
        }
    
        public float getX() {
            return x;
        }
    
        public void setX(float x) {
            this.x = x;
        }
    
        public float getY() {
            return y;
        }
    
        public void setY(float y) {
            this.y = y;
        }
    
        public ShapeDrawable getShape() {
            return shape;
        }
    
        public void setShape(ShapeDrawable shape) {
            this.shape = shape;
        }
    
        public int getColor() {
            return color;
        }
    
        public void setColor(int color) {
            this.color = color;
        }
    
        public RadialGradient getGradient() {
            return gradient;
        }
    
        public void setGradient(RadialGradient gradient) {
            this.gradient = gradient;
        }
    
        public float getAlpha() {
            return alpha;
        }
    
        public void setAlpha(float alpha) {
            this.alpha = alpha;
        }
    
        public Paint getPaint() {
            return paint;
        }
    
        public void setPaint(Paint paint) {
            this.paint = paint;
        }
    }
    

    animator/color_anim.xml

    <?xml version="1.0" encoding="utf-8"?>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="3000"
        android:propertyName="backgroundColor"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:valueFrom="#FF8080"
        android:valueTo="#8080FF"
        android:valueType="intType" />
    
    image.gif

    摘抄至《疯狂Android讲义(第4版)》

    相关文章

      网友评论

          本文标题:Android 属性动画

          本文链接:https://www.haomeiwen.com/subject/akfylrtx.html