自定义控件三部曲之动画篇(五)——ValueAnimator高级进阶(一)

前言:唯有脚踏实地,才能厚积薄发,未来只属于为梦想而奋斗的人们,今天的你决定未来的自己。

相关文章:

 

《Android自定义控件三部曲文章索引》http://blog.csdn.net/harvic880925/article/details/50995268

上一篇给大家介绍了ValueAnimator的大部分函数的用法,不过还都是些简单的用法,这篇我们带大家来看看有关加速器、animator和keyFrame的知识。

一、插值器

插值器,也叫加速器;有关插值器的知识,我在《Animation动画详解(二)——Interpolator插值器》中专门讲过,大家可以先看看这篇文章中各个加速器的效果。
这里再讲一下什么是插值器。我们知道,我们通过ofInt(0,400)定义了动画的区间值是0到400;然后通过添加AnimatorUpdateListener来监听动画的实时变化。那么问题来了,0-400的值是怎么变化的呢?像我们骑自行车,还有的快有的慢呢;这个值是匀速变化的吗?如果是,那我如果想让它先加速再减速的变化该怎么办?
这就是插值器的作用!插值器就是用来控制动画区间的值被如何计算出来的。比如LinearInterpolator插值器就是匀速返回区间点的值;而DecelerateInterpolator则表示开始变化快,后期变化慢;其它都类似,下面我们就看看ValueAnimator中插值器的应用方法,然后通过自定义一个插值器来看看插值器到底是什么。

1、使用插值器

我们就以BounceInterpolator(弹跳插值器)为例做一个实验,BounceInterpolator的解释是动画结束的时候弹起;我们来看一下效果:

 

下面我们就来看一下利用ValueAnimator是如何实现的。布局和上一篇的都一样,这里就不再细讲了,我们着重来看一下,当点击start anim按钮以后是怎么做的,代码如下:

 

ValueAnimator animator = ValueAnimator.ofInt(0,600);

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int curValue = (int)animation.getAnimatedValue();
        tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());
    }
});
animator.setDuration(1000);
animator.setInterpolator(new BounceInterpolator());
animator.start();

如果大家已经看懂了上篇,这段代码就非常容易理解了,在监听中,我们只改变textview的top和bottom的位置,让它跟着当前动画的值来改变它当前的top和bottom的位置。然后我们利用setDuration(1000)给它设置上做一次动画所需要的时长,然后通过setInterpolator()给它设置插值器,也就是过渡值变化的规则;
从效果图中也可以看出,插值器的意义其实就相当于物理公式中的加速度参数,所以这也就是它也叫加速器的原因。

源码在文章底部给出 

在学会了怎么使用加速器以后,我们来看看如何自定义一个加速器吧

2、自定义加速器

1、概述

在这段,我们就开始着手自己写一个加速器了,在写加速器之前,先看看人家的加速器是怎么写的吧。
先看看LinearInterpolator:

public class LinearInterpolator implements Interpolator {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return input;
    }
}
public interface Interpolator extends TimeInterpolator {
}

LinearInterpolator实现了Interpolator接口;而Interpolator接口则直接继承自TimeInterpolator,而且并没有添加任何其它的方法。
那我们来看看TimeInterpolator接口都有哪些函数吧:

/**
 * A time interpolator defines the rate of change of an animation. This allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
public interface TimeInterpolator {

    /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}

这里是TimeInterpolator的代码,它里面只有一个函数float getInterpolation(float input);我们来讲讲这个函数是干什么的。
参数input:input参数是一个float类型,它取值范围是0到1,表示当前动画的进度,取0时表示动画刚开始,取1时表示动画结束,取0.5时表示动画中间的位置,其它类推。
返回值:表示当前实际想要显示的进度。取值可以超过1也可以小于0,超过1表示已经超过目标值,小于0表示小于开始位置。
对于input参数,它表示的是当前动画的进度,匀速增加的。什么叫动画的进度,动画的进度就是动画在时间上的进度,与我们的任何设置无关,随着时间的增长,动画的进度自然的增加,从0到1;input参数相当于时间的概念,我们通过setDuration()指定了动画的时长,在这个时间范围内,动画进度肯定是一点点增加的;就相当于我们播放一首歌,这首歌的进度是从0到1是一样的。
而返回值则表示动画的数值进度,它的对应的数值范围是我们通过ofInt(),ofFloat()来指定的,这个返回值就表示当前时间所对应的数值的进度。
我们先看看下面这段代码ÿ

  • 102
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 48
    评论
好的,以下是一个简单的沙漏动画自定义控件的实现过程: 1. 创建一个自定义控件类SandClockView,继承自View。 2. 在构造函数中初始化画笔Paint和属性动画AnimatorSet,用于控制沙漏动画的进度。 3. 重写onMeasure方法,确保控件的宽高比为2:3,并且高度不超过屏幕高度的一半。 4. 重写onDraw方法,在画布上绘制两个三角形和一条线段,形成沙漏的形状。 5. 创建方法startAnimation和stopAnimation,分别用于开始和停止沙漏动画。在startAnimation方法中,创建两个ValueAnimator对象,分别控制沙漏上下部分的动画进度,然后将它们添加到AnimatorSet中,设置动画时间和循环次数,并启动动画。 6. 在onDetachedFromWindow方法中停止动画。 以下是完整代码实现: ``` public class SandClockView extends View { private Paint mPaint; private AnimatorSet mAnimatorSet; private float mTopLeftX; private float mTopLeftY; private float mBottomLeftX; private float mBottomLeftY; private float mRightX; private float mTopY; private float mBottomY; private int mWidth; private int mHeight; public SandClockView(Context context) { super(context); init(); } public SandClockView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SandClockView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLACK); mPaint.setStrokeWidth(5); mAnimatorSet = new AnimatorSet(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); // 保证宽高比为2:3 if (width * 3 > height * 2) { width = height * 2 / 3; } else { height = width * 3 / 2; } // 保证高度不超过屏幕高度的一半 int screenHeight = getResources().getDisplayMetrics().heightPixels; if (height > screenHeight / 2) { height = screenHeight / 2; width = height * 2 / 3; } mWidth = width; mHeight = height; setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { float triangleWidth = mWidth / 3f; float triangleHeight = mHeight / 2f; mTopLeftX = (mWidth - triangleWidth) / 2f; mTopLeftY = (mHeight - triangleHeight) / 2f; mBottomLeftX = mTopLeftX; mBottomLeftY = mTopLeftY + triangleHeight; mRightX = mTopLeftX + triangleWidth; mTopY = mTopLeftY; mBottomY = mBottomLeftY; Path path = new Path(); path.moveTo(mTopLeftX, mTopLeftY); path.lineTo(mRightX, mTopY); path.lineTo(mBottomLeftX, mBottomLeftY); path.lineTo(mTopLeftX, mTopLeftY); path.lineTo(mRightX, mBottomY); path.lineTo(mBottomLeftX, mBottomLeftY); canvas.drawPath(path, mPaint); } public void startAnimation() { ValueAnimator topAnimator = ValueAnimator.ofFloat(mTopY, mBottomY); topAnimator.setInterpolator(new LinearInterpolator()); topAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mTopY = (float) animation.getAnimatedValue(); invalidate(); } }); ValueAnimator bottomAnimator = ValueAnimator.ofFloat(mBottomY, mTopY); bottomAnimator.setInterpolator(new LinearInterpolator()); bottomAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mBottomY = (float) animation.getAnimatedValue(); invalidate(); } }); mAnimatorSet.play(topAnimator).with(bottomAnimator); mAnimatorSet.setDuration(1000); mAnimatorSet.setStartDelay(500); mAnimatorSet.setRepeatCount(ValueAnimator.INFINITE); mAnimatorSet.start(); } public void stopAnimation() { mAnimatorSet.cancel(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); stopAnimation(); } } ``` 使用时,可以在布局文件中添加一个SandClockView控件,并在代码中调用startAnimation方法开始动画,调用stopAnimation方法停止动画。例如: ``` SandClockView sandClockView = findViewById(R.id.sand_clock_view); sandClockView.startAnimation(); ``` 注意:以上代码只是一个简单的实现,实际应用中可能需要根据具体需求进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 48
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值