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

概述:

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)(百度糯米加载动画示例)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容