安卓自定义View——(一:RaiseNumberAnimTextView 带数字增长动画的TextView)

前言

某些app上,新进入一个Activity的时候,上面的一个关键性数字(比如金额)会以一个数字不断变大的动画来显示。刚开始的时候,想到的一个方案是:使用Thead+Handler,给定一个动画总时长与刷新间隔时长,根据公式(数字从0开始,每次增长值为数组除以动画执行次数,动画执行次数等于动画总时长除以刷新间隔时长):

// addValue 每次刷新时数值增加多少;number 最终要显示的数字
// alltime 动画执行总时长;gaptime 动画刷新时间间隔
addValue=number/(alltime/gaptime);

每隔一段时间重新设置TextView的字符串为增加后的值,直到动画结束显示最终结果。
其实对安卓动画有一定了解的应该都知道ValueAnimator这个类,我们可以使用它来很好的实现所要的效果,而不需要我们自己来生硬的控制隔多久就增加多少刷新显示。根据ValueAnimator的属性方法以及实际需要,我们封装一个自定义View来实现我们的需求:

  1. 能自定义设置动画持续时长。
  2. 能设置int型数字或float型数字。
  3. 能控制动画速率,如匀速增加、加速、减速、先加速后减速等。
  4. 能监听到动画正常的执行结束,调用后续方法。
  5. 跟随界面的生命周期,相应的执行动画的结束、暂停、继续等操作。

RaiseNumberAnimTextView代码

根据暂时的需求,RaiseNumberAnimTextView的代码如下所示,继承自TextView,所以它拥有TextView的所有属性以及方法,其他部分的介绍见注释:

/**
 * API不能低于19。
 * setDuration方法设置动画执行时长
 * setNumberWithAnim方法调用时将会以递增动画显示数字出来
 * setAnimEndListener方法设置监听动画正常执行结束
 * setAnimInterpolator方法设置动画速率
 * 在该View的Activity或Fragment的onDestroy方法中调用clearAnimator方法,onPause调用onPause,onResume调用onResume。
 */
public class RaiseNumberAnimTextView extends TextView {
    private long mDuration = 1000; // 动画持续时间 ms,默认1s

    private ValueAnimator animator;

    private TimeInterpolator mTimeInterpolator = new LinearInterpolator(); // 动画速率

    private AnimEndListener mEndListener; // 动画正常结束监听事件

    public RaiseNumberAnimTextView(Context context) {
        super(context);
    }

    public RaiseNumberAnimTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RaiseNumberAnimTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * 设置动画持续时间,默认为1s。需要在setNumberWithAnim之前设置才有效
     * @param duration
     */
    public void setDuration(long duration) {
        if (duration > 0) {
            mDuration = duration;
        }
    }

    /**
     * 设置动画速率,默认为LinearInterpolator。需要在setNumberWithAnim之前设置才有效
     * @param timeInterpolator
     */
    public void setAnimInterpolator(TimeInterpolator timeInterpolator) {
        mTimeInterpolator = timeInterpolator;
    }

    /**
     * 设置要显示的float数字,带动画显示
     * @param number
     */
    public void setNumberWithAnim(float number) {
        clearAnimator();

        // 设置动画,float值的起始值
        animator = ValueAnimator.ofFloat(0.0f, number);

        startAnimator();
    }

    /**
     * 设置要显示的int数字,带动画显示。
     * @param number
     */
    public void setNumberWithAnim(int number) {
        clearAnimator();

        // 设置动画,int值的起始值
        animator = ValueAnimator.ofInt(0, number);

        startAnimator();
    }

    // 清除动画
    public void clearAnimator() {
        if (null != animator) {
            if (animator.isRunning()) {
                animator.removeAllListeners();
                animator.removeAllUpdateListeners();
                animator.cancel();
            }
            animator = null;
        }
    }

    // 暂停动画
    public void onPause() {
        if (null != animator && animator.isRunning()) {
            animator.pause(); // API 不低于19
        }
    }

    // 继续执行动画
    public void onResume() {
        if (null != animator && animator.isRunning()) {
            animator.resume();
        }
    }

    // 设置时常与过程处理,启动动画
    private void startAnimator() {
        if (null != animator) {
            animator.setDuration(mDuration);

            animator.setInterpolator(mTimeInterpolator);

            // 动画过程中获取当前值,显示
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    setText(valueAnimator.getAnimatedValue().toString());
                }
            });
            animator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                }

                @Override
                public void onAnimationEnd(Animator animator) {
                    if (null != mEndListener) { // 动画不是中途取消,而是正常结束
                        mEndListener.onAnimFinish();
                    }
                }

                @Override
                public void onAnimationCancel(Animator animator) {
                }

                @Override
                public void onAnimationRepeat(Animator animator) {
                }
            });
            animator.start();
        }
    }

    public void setAnimEndListener(AnimEndListener listener) {
        mEndListener = listener;
    }

    // 动画显示数字的结束监听,当动画结束显示正确的数字时,可能需要做些处理
    public interface AnimEndListener {
        void onAnimFinish();
    }
}

需要注意的有两点:
1. 在界面的onDestroy方法中调用clearAnimator方法,关闭该View的动画,释放资源。测试发现,界面被关掉后,该View的动画与动画结束监听还在执行。
2. 在界面的onPause调用该View的onPause,onResume调用该View的onResume。但要注意,ValueAnimator的pause()方法要求api最低版本不能低于19。低版本中则需要自己去实现动画的暂停与继续效果。另外这里也说一下为什么要加这个动画的暂停与继续,假如进入界面显示这个数字,动画执行完之后我们要弹一个Toast或者弹出一个对话框,那么我们肯定不希望当我们进入了下一个界面的时候,动画才结束这个时候做出那些操作。如果没有这些顾虑的话,可以不做这个步骤。

测试验证

使用三个RaiseNumberAnimTextView来测试,第一个和第二个均设置显示1000000,且动画时长均为5s,第一个加速显示,第二个减速显示,且第一个添加动画正常结束的监听。第三个则使用默认的速率与动画时长,显示123456.78。且在执行动画时切换界面测试动画的暂停与继续。测试代码如下(onResume、onPause、onDestroy等方法代码省略,记得要加上):

tvRaisenumber1.setAnimEndListener(new RaiseNumberAnimTextView.AnimEndListener() {
    @Override
    public void onAnimFinish() {
        ToastUtils.showToast("第一个数字的动画执行完了");
    }
});
// 测试更改值会不会异常
tvRaisenumber1.setDuration(1000 * 15);
tvRaisenumber1.setNumberWithAnim(123456.95f);
tvRaisenumber1.setDuration(1000 * 5);
tvRaisenumber1.setAnimInterpolator(new AccelerateInterpolator());
tvRaisenumber1.setNumberWithAnim(1000000);

tvRaisenumber2.setDuration(1000 * 5);
tvRaisenumber2.setAnimInterpolator(new DecelerateInterpolator());
tvRaisenumber2.setNumberWithAnim(1000000);

tvRaisenumber3.setNumberWithAnim(123456.78f);

经测试,运行正常。
这里写图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值