自定义View之动画篇-属性动画

文章首发于个人博客
任何形式的转载都请联系作者获得授权并注明出处。

一、基本概念

属性动画就是在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果。

二、动画

1、View中的方法

我们可以对View调用以下属性实现动画

View中的方法 功能
setTranslationX()setTranslationY()setTranslationZ() 设置X、Y、Z轴平移
setX()setX()setX() 设置X、Y、Z轴绝对位置
setRotation()setRotationX()setRotationY() 设置平面、X、Y轴旋转
setScaleX()setScaleY() 设置X、Y方向缩放
setAlpha() 设置透明度

2、ValueAnimator

属性动画机制中最核心的一个类,ObjectAnimator和ViewPropertyAnimator都是通过这个这个实现的。ValueAnimator是通过不断控制值的变化,再不断添加属性,从而实现动画效果

使用方法

  1. 初始化ValueAnimator,设置各种属性
  2. 给ValueAnimator设置监听器,通过getAnimatedValue()拿到变化值后更新控件。
常用方法 功能
ofInt() 返回一个int型变化的ValueAnimator
ofFloat() 返回一个float型变化的ValueAnimator
ofObject 返回一个object型变化的ValueAnimator。

1.1、设置颜色变化

我们这里并没有使用ofArgb这个属性,因为这个方法是在API21以上使用的,在低版本上不兼容。

final View view = findViewById(R.id.view);
//以整型数值的形式,过渡到结束值
ValueAnimator animator = ValueAnimator.ofInt(0xffff00ff, 0xffffff00, 0xffff00ff);
//设置求值器
animator.setEvaluator(new ArgbEvaluator());
//设置动画的播放时长
animator.setDuration(1000);
//设置动画重复播放次数,ValueAnimator.INFINITE为无限重复
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int currentValue = (Integer) animation.getAnimatedValue();
         Log.i(TAG, "onAnimationUpdate: "+currentValue);
         view.setBackgroundColor(currentValue);
         view.requestLayout();
    }
});
animator.start();

1.2、ofObject()的使用

ofInt和ofFloat是针对Int值和Float值的变化,但是,我们只能控制一个值的变化,但是当我们需要实现多值变化时,它们就不再满足我们的需求。

//设置变化的值
ValueObject startObjectVal = new ValueObject(1f, 0);
ValueObject endObjectVal = new ValueObject(0f, 360);
ValueAnimator animator = ValueAnimator.ofObject(new Evaluator(), startObjectVal, endObjectVal);
animator.setDuration(3000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        view.setAlpha(((ValueObject) animation.getAnimatedValue()).alphaValue);
        //可以设置各种动画
        view.setRotation(((ValueObject) animation.getAnimatedValue()).rotateValue);
        view.requestLayout();
    }
});
animator.start();

》》设置估值器,估值器的作用觉得值得变化顺序《《

//下面具体讲解
class Evaluator implements TypeEvaluator<ValueObject> {
    @Override
    public ValueObject evaluate(float fraction, ValueObject startValue, ValueObject endValue) {
        float alphaValue = startValue.alphaValue + (endValue.alphaValue - startValue.alphaValue) *          fraction;
        float rotateValue = startValue.rotateValue + (endValue.rotateValue - startValue.rotateValue) * fraction;
        return new ValueObject(alphaValue, rotateValue);
    }
}

//创建对象,保存透明度和旋转的值得变化
class ValueObject {
    float alphaValue;//透明度
    float rotateValue;//旋转

    ValueObject(float alphaValue, float rotateValue) {
        this.alphaValue = alphaValue;
        this.rotateValue = rotateValue;
    }
}

1.3、属性和方法整理

常用的属性

方法 解释 方法 解释
setDuration 设置动画总时长,单位毫秒。 getDuration 获取动画总时长
setFrameDelay 设置每一帧之间间隔多少毫秒 getFrameDelay 获取每一帧之间间隔多少毫秒
setInterpolator 设置动画的插值器 getInterpolator 获取当前使用的插值器
setRepeatCount 设置重复次数 getRepeatCount 获取重复次数
setRepeatMode 设置重复模式。有RESTART(正序)和REVERSE(倒序) getRepeatMode 获取重复模式
setStartDelay 设置开始前延迟毫秒数 getStartDelay 获取开始前延迟毫秒数
getAnimatedValue 获取计算出来的当前属性值 getAnimatedValue 获取计算出来的当前某个属性的值
setEvaluator 设置求值器 setFloatValues 设置Float型变化值,设置初始值、中间值、结束值
setIntValues 设置Int型变化值,设置初始值、中间值、结束值 setObjectValues 设置Object型变化值,设置初始值、中间值、结束值

常用的方法

方法 解释 方法 解释
addUpdateListener 添加值变化监听器 addUpdateListener 添加动画状态监听器
start 开始动画 pause 暂停动画
resume 继续动画 cancel 取消动画
end 动画结束 reverse 倒序播放动画
isRunning 判断是否在正在运行 isStarted 判断是的开始播放

1.4、估值器和插值器(重点)

估值器--TypeEvaluator:决定值的具体变化顺序

估值器的使用我们上面已经学习过了,在这里对估值器的具体使用简单的解释。

//实现了TypeEvaluator接口
public class FloatEvaluator implements TypeEvaluator {  
    
    /**
    * fraction:表示动画完成度
    * startValue:动画的初始值
    * endValue:动画的结束值
    * /
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        //用初始值加上动画完成度乘以结束值和初始值之间的差值
    }  
}

插值器--Interpolator:决定值的速度变化顺序

使用方法:

setInterpolator(Interpolator interpolator)//设置速度插值器

常用的插值器:

  • LinearInterpolator:线性匀速变化
  • AccelerateDecelerateInterpolator:先加速再减速
  • AccelerateInterpolator:持续加速
  • DecelerateInterpolator:持续减速直到 0
  • AnticipateInterpolator:先回拉一下再进行正常动画轨迹
  • OvershootInterpolator:动画会超过目标值一些,然后再弹回来
  • AnticipateOvershootInterpolator:先回拉一下再进行正常动画轨迹,最后动画会超过目标值一些,然后再弹回来
  • BounceInterpolator:在目标值来回跳动

3、ObjectAnimator

直接对对象的属性值进行改变操作,从而实现动画效果,内部是有ValueAnimator实现,因此其也有ofXXX()方法,这里接受的参数不一样了,具体的就不整理了,常用属性和常用方法和ValueAnimator一致

使用方法:

  1. 对于自定义控件,使用setter / getter 方法;
  2. 调用 ObjectAnimator.ofXXX() 创建 ObjectAnimator 对象,设置常用属性。
  3. 调用 start() 方法执行动画

方法:ofFloat(Object target, String propertyName, float... values)

参数:

  • target:我们需要控制动画的对象

  • propertyName:设置动画效果(传入任意属性值)

    这里传入的参数,其实是调用了ValueAnimatorsetXXX()方法和getXXX()方法,具体的代码就不贴了。

    • alpha:透明度
    • rotationRotationXRotationY:旋转
    • translationXtranslationY:平移
    • ScaleXscaleY :缩放
    • values:自定义属性
  • values:值

》》自定义圆形进度条《《

完整代码在线地址:Github在线地址

ProPress.java

我们在Activity中,给动画设置了一个属性progress,我们在ProPress.java中,用getProgress()setProgress方法,这样系统会自动调用该对象属性的set() & get()方法进行赋值。

/**
 * Created by Active_Loser on 2018/10/22.
 * Content: 自定义圆形进度条
 */
public class ProPress extends View {

    //.....
    private int progress = 0;

    public float getProgress() {
        return progress;
    }

    public void setProgress(int progress) {
        this.progress = progress;
        invalidate();
    }
    
    //.....
    
    @Override
    protected void onDraw(Canvas canvas) {
        int width = getWidth();
        int height = getHeight();
        //绘制圆弧
        RectF arcRectF = new RectF(20, 20, width-20, height-20);
        //progress设置值为0-100,因此需要乘3.6
        canvas.drawArc(arcRectF, 135, progress * 3.6f, false, mArcPaint);
        canvas.drawText(progress + "%", width/2, height/2-(mArcPaint.ascent() + mArcPaint.descent()) / 2, mTextPaint);
    }
}

XML:

<com.example.active.loser.views.level3.ProPress
    android:layout_width="300dp"
    android:layout_height="300dp"
    android:layout_centerInParent="true"
    android:id="@+id/view"/>

Activity:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ProPress view = findViewById(R.id.view);
    // 创建 ObjectAnimator 对象
    ObjectAnimator animator = ObjectAnimator.ofInt(view, "progress", 0, 100);
    //持续时长4秒
    animator.setDuration(4000);
    animator.setInterpolator(new FastOutSlowInInterpolator());
    animator.start();
}

4、ViewPropertyAnimator

ValueAnimator、ObjectAnimator、ViewPropertyAnimator三者使用难度和灵活性逐渐递减,因此我们尽量选择简单的使用

使用方法

view.animate().动画();  

常用的动画如下表所示:

View中的方法 对于中的方法ViewPropertyAnimator 功能
setTranslationX() translationX()translationXBy() 设置X轴平移
setTranslationY() translationY()translationYBy() 设置Y轴平移
setTranslationZ() translationZ()translationZBy() 设置Z轴平移
setX()setX()setX() x()xBy()y()yBy()z()zBy() 设置X、Y、Z轴绝对位置
setRotation() rotation()rotationBY() 设置平面轴旋转
setRotationX() rotationX()rotationXBy() 设置X轴旋转
setRotationY() rotationY()rotationYBy() 设置Y轴旋转
setScaleX() scaleX()scaleX()By 设置X方向缩放
setScaleY() scaleY()scaleYBy() 设置Y方向缩放
setAlpha() alpha()alphaBy() 设置透明度

特点:

  • by:变化偏移、无by:变化到

    即:加上By的意思是,继续动画这么多数值,不加By的意思是动画到这个数值。

简单的使用:

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

推荐阅读更多精彩内容