美文网首页UI效果仿写自定义Android自定义View
动画(Property Animation)(闪闪星光示例+知识

动画(Property Animation)(闪闪星光示例+知识

作者: 一字节溢出 | 来源:发表于2016-11-25 16:25 被阅读369次

    概述:

    Property Animation,属性动画非常强大,Android官方API如是说:属性动画系统是一个强大的框架,它几乎可以将动画添加到任何东西上。它是在View Animation之后引入的,那么相比较有什么优势:
    1,Property Animation不仅可以使View收缩,旋转而且可以改变View的color,size等属性值。而且作用范围不局限于View,而是任何对象。
    2,Property Animation使视图发生了改变同时View的真实位置也发生了改变。
    3,更少的代码做更多的事情。

    详解:

    了解Property Animation可以从下面三个方面来了解:

    1,Animators

    Animator相关类为我们提供了创建属性动画的方法。涉及到的类如下图所示:


    Paste_Image.png

    Animator:为我们提供了一些基础的方法,如添加监听,开始,取消动画等方法。

    ValueAnimator:提供了一个简单的计时引擎,通过计算动画值并将它们设置在目标对象上, 用于运行动画。可以用代码定义,也可以用xml,个人觉得在代码中比较常用,这里列举下在代码中创建的方法:

    ValueAnimator()
    static ValueAnimator ofArgb(int... values)
    static ValueAnimator ofFloat(float... values)
    static ValueAnimator ofInt(int... values)
    static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
    static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)
    

    ObjectAnimator: ValueAnimator的这个子类提供了对目标对象上的动画属性的支持。View对象对应的propertyName一般有这几个:alpha, translationX,translationY,x,y,rotation,rotationX,rotationY,scaleX,scaleY。如果记不住可在View的源码中查看。

    ObjectAnimator()
    static ObjectAnimator ofInt(Object target, String propertyName, int... values)
    static ObjectAnimator ofArgb(Object target, String propertyName, int... values)
    static ObjectAnimator ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)
    ...
    

    TimeAnimator: 比ValueAnimator多了一个TimeListener,用于监听执行总时间以及当前帧和上一帧的时差。

    //示例     
    timeAnim.setTimeListener(new TimeAnimator.TimeListener() {
                @Override
                public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
                }
    });
    

    AnimatorSet:可以将多个Animator一起或者按照一定的顺序执行。

    //示例
    AnimatorSet anim = new AnimatorSet();
    anim.play(rotateAnim).after(transferAnim).with(timeAnim);
    anim.start();
    
    2,Evaluators

    赋值器告诉属性动画系统如何计算给定属性的值。Android提供了下面这些赋值器。


    Paste_Image.png
    //这是Android中IntEvaluator实现代码我们也可以通过继承TypeEvaluator来实现自己的Evaluator。关键重载
    evaluate方法就可以了。
    public class IntEvaluator implements TypeEvaluator<Integer> {
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            int startInt = startValue;
            return (int)(startInt + fraction * (endValue - startInt));
        }
    }
    

    ValueAnimator提供了setEvaluator(TypeEvaluator value)来设置赋值器。那计算出来的值我们也可以在下面的回调中通过来getAnimatedValue()获取。

        transferAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    valueAnimator.getAnimatedValue();
                }
            });
    
    2,Interpolators

    时间插值器定义动画中的具体值如何作为时间的函数计算。通俗讲就是用什么样的速率(匀速,变速等待)来展示。Android提供了下面这些插值器,基本上见名知意。

    Paste_Image.png
    //这是Android中AccelerateInterpolator 实现代码,我们也可以通过继承Interpolator 来实现自己的Interpolator ,关键重载getInterpolation(float input)方法就可以了。
    public class AccelerateInterpolator implements Interpolator {
        private final float mFactor;
        private final double mDoubleFactor;
    
        public AccelerateInterpolator() {
            mFactor = 1.0f;
            mDoubleFactor = 2.0;
        }
    
        public AccelerateInterpolator(float factor) {
            mFactor = factor;
            mDoubleFactor = 2 * mFactor;
        }
        
        public AccelerateInterpolator(Context context, AttributeSet attrs) {
            TypedArray a =
                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AccelerateInterpolator);
            
            mFactor = a.getFloat(com.android.internal.R.styleable.AccelerateInterpolator_factor, 1.0f);
            mDoubleFactor = 2 * mFactor;
    
            a.recycle();
        }
        
        public float getInterpolation(float input) {
            if (mFactor == 1.0f) {
                return input * input;
            } else {
                return (float)Math.pow(input, mDoubleFactor);
            }
        }
    }
    
    

    ValueAnimator提供了setInterpolator和getInterpolator方法来设置插值器。

    示例:

    通过上面的文字,我们了解了创建属性动画的基本类和方法,我们通过示例来进一步熟悉用法。
    猎豹清理大师通知栏清理界面有一个闪星星的动画效果,我们实现下后效果如下图:

    ezgif.com-ddf5c08218.gif
    实现代码

    首先新建一个ShiningStar.java的类。

    public class ShiningStar extends View {
    
        /**
         * 星星的坐标及大小
         */
        private static final int[][] starPosition = new int[][] {
                {80, 80, 66},{160, 80, 80},{240,160, 100},{120, 240, 120},{360, 480, 66}, {600, 600, 120}, {720, 500, 120},
                {360, 100, 66}, {600, 160, 120}, {720, 240, 120},{860, 80, 80}
        };
    
        /**
         * 星星存储器
         */
        private List<Star> stars = new ArrayList<Star>();
    
        /**
         * 星星资源
         */
        private Bitmap bitmap = null;
    
        /**
         * 画笔
         */
        private Paint paint = null;
    
        public ShiningStar(Context context) {
            super(context);
            init();
        }
    
        public ShiningStar(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public ShiningStar(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (bitmap != null) {
                for (int i = 0; i < stars.size(); i++) {
                    canvas.save();//这样使每一个星星的状态独立
                    Rect dst = new Rect(stars.get(i).x, stars.get(i).y, stars.get(i).x + stars.get(i).size, stars.get(i).y + stars.get(i).size);
                    canvas.scale(stars.get(i).scale,  stars.get(i).scale, stars.get(i).x + stars.get(i).size/2, stars.get(i).y + stars.get(i).size/2);
                    paint.setAlpha((int)stars.get(i).alpha);
                    canvas.drawBitmap(bitmap, null, dst, paint);
                    canvas.restore();
                }
            }
        }
    
        /**
         * 初始化
         */
        private void init() {
            initStars();
            initAnimation();
        }
    
        /**
         * 初始化星星对象
         */
        private void initStars() {
            for (int i = 0; i < starPosition.length; i++) {
                final Star star = new Star();
                star.x = starPosition[i][0];
                star.y = starPosition[i][1];
                star.size = starPosition[i][2];
                star.scale = 1;
                star.alpha = 255;
                stars.add(star);
            }
        }
    
        /**
         * 初始化动画及绘制元素的对象
         */
        private void initAnimation() {
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.star_icon);
            paint = new Paint();
            paint.setAlpha(255);
    
            ValueAnimator scaleAnim = ValueAnimator.ofFloat(0, 255, 0);
            scaleAnim.setInterpolator(new LinearInterpolator());
            scaleAnim.setDuration(1000);
            scaleAnim.setRepeatCount(-1);
            scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    boolean flag = false;
                    for (int i = 0; i < stars.size(); i++) {
                        if (flag) {
                            stars.get(i).scale = ((float)animation.getAnimatedValue())/255;
                            stars.get(i).alpha = (float)animation.getAnimatedValue();
                        } else {
                            stars.get(i).scale = 1 - ((float)animation.getAnimatedValue())/255;
                            stars.get(i).alpha = 255 - (float)animation.getAnimatedValue();
                        }
                        flag = !flag;
                    }
                    postInvalidate();
                }
            });
            scaleAnim.start();
        }
    
        /**
         * 星星属性
         */
        class Star {
            int x;
            int y;
            int size;
            float scale;
            float alpha;
        }
    }
    
    

    2,新建一个layout文件。

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:background="#2196F3"
        tools:context="com.wayne.android.viewanimation.MainActivity">
    
        <com.wayne.android.viewanimation.ShiningStar
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:layout_centerHorizontal="true"/>
    </RelativeLayout>
    

    3,在Activity中引用

    setContentView(R.layout.activity_main);
    

    这个例子中我们用到了ValueAnimator 来实现星星的闪动效果。那我们能否改造一下使用ObjectAnimator呢,当然可以。

    public class ShiningStar extends View {
    
        /**
         * 星星的坐标及大小
         */
        private static final int[][] starPosition = new int[][] {
                {80, 80, 66},{160, 80, 80},{240,160, 100},{120, 240, 120},{360, 480, 66}, {600, 600, 120}, {720, 500, 120},
                {360, 100, 66}, {600, 160, 120}, {720, 240, 120},{860, 80, 80}
        };
    
        /**
         * 星星存储器
         */
        private List<Star> stars = new ArrayList<Star>();
    
        /**
         * 星星资源
         */
        private Bitmap bitmap = null;
    
        /**
         * 画笔
         */
        private Paint paint = null;
    
        protected  float starScale = 1f;
    
        protected float starAlpha = 255f;
    
        public ShiningStar(Context context) {
            super(context);
            init();
        }
    
        public ShiningStar(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public ShiningStar(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (bitmap != null) {
                boolean flag = false;
                for (int i = 0; i < stars.size(); i++) {
                    canvas.save();//这样使每一个星星的状态独立
                    Rect dst = new Rect(stars.get(i).x, stars.get(i).y, stars.get(i).x + stars.get(i).size, stars.get(i).y + stars.get(i).size);
                    android.util.Log.i("TestShining", "scale: " + starScale);
                    if (flag) {
                        canvas.scale(starScale,  starScale, stars.get(i).x + stars.get(i).size/2, stars.get(i).y + stars.get(i).size/2);
                        paint.setAlpha((int)starAlpha);
                    } else {
                        canvas.scale(1 - starScale,  1 - starScale, stars.get(i).x + stars.get(i).size/2, stars.get(i).y + stars.get(i).size/2);
                        paint.setAlpha((int)(255 - starAlpha));
                    }
                    flag = !flag;
                    canvas.drawBitmap(bitmap, null, dst, paint);
                    canvas.restore();
                }
            }
        }
    
        /**
         * 初始化
         */
        private void init() {
            initStars();
            initAnimation();
        }
    
        /**
         * 初始化星星对象
         */
        private void initStars() {
            for (int i = 0; i < starPosition.length; i++) {
                final Star star = new Star();
                star.x = starPosition[i][0];
                star.y = starPosition[i][1];
                star.size = starPosition[i][2];
                stars.add(star);
            }
        }
    
        public float getStarScale() {
            return starScale;
        }
    
        public void setStarScale(float starScale) {
            this.starScale = starScale;
            postInvalidate();
        }
    
        public float getStarAlpha() {
            return starAlpha;
        }
    
        public void setStarAlpha(float starAlpha) {
            this.starAlpha = starAlpha;
            postInvalidate();
        }
    
        /**
         * 初始化动画及绘制元素的对象
         */
        private void initAnimation() {
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.star_icon);
            paint = new Paint();
            paint.setAlpha(255);
    
            ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(this, "starScale", 0, 1, 0);
            scaleAnim.setInterpolator(new LinearInterpolator());
            scaleAnim.setDuration(1000);
            scaleAnim.setRepeatCount(-1);
    
            ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, "starAlpha", 0, 255, 0);
            alphaAnim.setInterpolator(new LinearInterpolator());
            alphaAnim.setDuration(1000);
            alphaAnim.setRepeatCount(-1);
    
            AnimatorSet animatorSet = new AnimatorSet();
            animatorSet.playTogether(scaleAnim, alphaAnim);
            animatorSet.playTogether(scaleAnim);
            animatorSet.start();
        }
    
        /**
         * 星星属性
         */
        class Star {
            int x;
            int y;
            int size;
        }
    }
    
    

    从上面代码中我们要注意,在ShiningStar 类中增加了两个属性starScale 和starAlpha 并且增加了setter和getter方法,在set中View面进行刷新。这样ObjectAnimator才可以对这两个属性进行操作并且传递给界面。

    结语:

    Property Animation比较常用,本片主要总结了下知识点,示例并不全面。后面文章会有更多应用。

    上篇:动画(Drawable Animation)(百度糯米加载动画示例)

    相关文章

      网友评论

        本文标题:动画(Property Animation)(闪闪星光示例+知识

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