仿网易邮箱大师进度框

仿网易邮箱大师进度框

最近用上了网易邮箱大师,发现整个APP的风格非常简洁美观。特别喜欢它的进度框,就尝试用属性动画做了一个,算是对属性动画学习的实践。

先上效果图:

这里写图片描述

图像构成

这个图像由三段弧线构成。用到的方法是canvas.drawArc()需要注意的是paint的设置。

    // 抗锯齿
    paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    // 默认颜色
    paint.setColor(Color.parseColor("#B33425"));
    // 设置为只画线不填充
    paint.setStyle(Paint.Style.STROKE);
    // 设置默认的线宽
    paint.setStrokeWidth(strokWidth);
    // 将线的两头设置为半圆
    paint.setStrokeCap(Paint.Cap.ROUND);

绘制这三段弧线

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    if (animatorSet == null) {
        // 如果动画不存在,就启动动画
        startAnimation();
    }
    // 设置线宽
    paint.setStrokeWidth(strokWidth);

    // 画红色弧线
    paint.setColor(Color.parseColor("#B33425"));
    canvas.drawArc(getRectF(), 0f + angle, 80f, false, paint);

    // 隔开120度,再画灰线
    paint.setColor(Color.parseColor("#5D5C5A"));
    canvas.drawArc(getRectF(), 0f + angle + 120, 80f, false, paint);

    // 隔开240度, 再画蓝线
    paint.setColor(Color.parseColor("#2972A6"));
    canvas.drawArc(getRectF(), 0f + angle + 240, 80f, false, paint);
}

动画构成

仔细观察,可以看出整个动画由三个小动画组成:
1. 旋转,通过控制弧线起始角度
2. 进度条的粗细变化,通过控制paint的strokeWidth
3. 绘画空间的缩放,通过控制drawArc的RectF参数

定义三个域来保存数据,并通过动画操纵它们:

// 绘制角度
private float angle = 0;
// 线宽
private float strokWidth = 5;
// 距离边框的空白大小
private float padding = 0;

开始动画:

public void startAnimation() {

    // 简单的ValueAnimator,从0度到360度变化。
    ValueAnimator circleAnimator = ValueAnimator.ofFloat(0, 360);
    circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            angle = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    // 一直正向循环
    circleAnimator.setRepeatMode(ValueAnimator.RESTART);
    circleAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 控制线宽,从5像素到20像素
    ValueAnimator thickAnimator = ValueAnimator.ofFloat(5, 20);
    thickAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            strokWidth = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    // 反向循环
    thickAnimator.setRepeatMode(ValueAnimator.REVERSE);
    thickAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 控制绘制空间的空白变化,会让弧线变曲
    ValueAnimator paddingAnimator = ValueAnimator.ofFloat(10, 100);
    paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            padding = (float) animation.getAnimatedValue();
            invalidate();
        }
    });

    paddingAnimator.setRepeatMode(ValueAnimator.REVERSE);
    paddingAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 同时播放三个动画
    animatorSet = new AnimatorSet();
    animatorSet.play(circleAnimator).with(thickAnimator).with(paddingAnimator);
    animatorSet.setDuration(1500);
    animatorSet.start();
}

绘制空间的变化:

// 获取绘制空间,通过padding控制坐标
private RectF getRectF() {

    return new RectF(0f + strokWidth / 2 + padding, // 左
            0f + strokWidth / 2 + padding, // 上
            getWidth() - strokWidth / 2 - padding, // 右
            getWidth() - strokWidth / 2 - padding); // 下
}

附上全文件

/**
 * Created by JayChen on 2016/10/13.
 */

public class TripProgress extends View {

private Paint paint;

// 绘制角度
private float angle = 0;
// 线宽
private float strokWidth = 5;
// 距离边框的空白大小
private float padding = 0;

private AnimatorSet animatorSet;

public TripProgress(Context context, AttributeSet attrs) {
    super(context, attrs);

    // 抗锯齿
    paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    // 默认颜色
    paint.setColor(Color.parseColor("#B33425"));
    // 设置为只画线不填充
    paint.setStyle(Paint.Style.STROKE);
    // 设置默认的线宽
    paint.setStrokeWidth(strokWidth);
    // 将线的两头设置为半圆
    paint.setStrokeCap(Paint.Cap.ROUND);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    if (animatorSet == null) {
        // 如果动画不存在,就启动动画
        startAnimation();
    }
    // 设置线宽
    paint.setStrokeWidth(strokWidth);

    // 画红色弧线
    paint.setColor(Color.parseColor("#B33425"));
    canvas.drawArc(getRectF(), 0f + angle, 80f, false, paint);

    // 隔开120度,再画灰线
    paint.setColor(Color.parseColor("#5D5C5A"));
    canvas.drawArc(getRectF(), 0f + angle + 120, 80f, false, paint);

    // 隔开240度, 再画蓝线
    paint.setColor(Color.parseColor("#2972A6"));
    canvas.drawArc(getRectF(), 0f + angle + 240, 80f, false, paint);
}

public void startAnimation() {

    // 简单的ValueAnimator,从0度到360度变化。
    ValueAnimator circleAnimator = ValueAnimator.ofFloat(0, 360);
    circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            angle = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    // 一直正向循环
    circleAnimator.setRepeatMode(ValueAnimator.RESTART);
    circleAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 控制线宽,从5像素到20像素
    ValueAnimator thickAnimator = ValueAnimator.ofFloat(5, 20);
    thickAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            strokWidth = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    // 反向循环
    thickAnimator.setRepeatMode(ValueAnimator.REVERSE);
    thickAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 控制绘制空间的空白变化,会让弧线变曲
    ValueAnimator paddingAnimator = ValueAnimator.ofFloat(10, 100);
    paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            padding = (float) animation.getAnimatedValue();
            invalidate();
        }
    });

    paddingAnimator.setRepeatMode(ValueAnimator.REVERSE);
    paddingAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 同时播放三个动画
    animatorSet = new AnimatorSet();
    animatorSet.play(circleAnimator).with(thickAnimator).with(paddingAnimator);
    animatorSet.setDuration(1500);
    animatorSet.start();
}

// 获取绘制空间,通过padding控制坐标
private RectF getRectF() {

    return new RectF(0f + strokWidth / 2 + padding, // 左
            0f + strokWidth / 2 + padding, // 上
            getWidth() - strokWidth / 2 - padding, // 右
            getWidth() - strokWidth / 2 - padding); // 下
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值