自定义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();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容