android添加滚动条加载的动画效果,Android实现水波进度加载

之前写过一些比这个复杂的的自定义view,刚打算做这个的时候想,这个界面元素少,很快就画完了吧,那就预算3个小时搞定吧。结果有点小看了,花了我半天时间才做完,主要就是卡在了波浪平移的理解上,背景移动一定周期以后,恢复原位但要与当前背景重合,以达到连续移动的效果,这是做2D游戏常用的做法。

国际惯例,先上效果先上效果

83d929250d0e

实现思路

1.用path绘制一个封闭的水池以及水面的曲线波浪

2.用属性动画修改水池高度让水涨起来

3.让波,浪起来:画2倍view宽的波浪,实时改变path起始点,让波浪向右平移,动画结束回到原起点无限循环

4.绘制中心文字和滚动数字

6.绘制前景圆形,设置重叠显示模式,将外形呈圆形

代码实现过程

先过目一下总共要声明哪些变量常量。

/**

* 波浪paint

*/

private Paint wavePaint;

/**

* 圆环paint

*/

private Paint circlePaint;

/**

* 圆环paint

*/

private Paint ringPaint;

/**

* 文本paint

*/

private Paint textPaint;

private Path wavePath;

private int waveWidth;

/**

* 波浪幅度

*/

private float waveHeight;

/**

* 水面波浪数,在为偶数的情况下,波浪才会按周期平移

*/

private final int waveCount = 2;

/**

* 当前数值

*/

private int curNum;

/**

* 总数值

*/

private int totalNum;

/**

* 最终进度比例

*/

private float percent;

/**

* 当前轮水波移动比例

*/

private int movePercent;

/**

* 该view尺寸大小

*/

private int rectSize;

/** 动画时长 */

private final int ANIM_DURATION = 2000;

private float moveDistance;

/**

* 缓存bitmap

*/

private Bitmap bitmap;

private Canvas bitmapCanvas;

private int ringWidth;

/**

* 当前量

*/

private int num;

从-rectWidth位置开始为path起点,绘制一个波浪曲线加水池的一个梯形。

private Path setWavePath() {

wavePath.reset();

wavePath.moveTo(-moveDistance, rectSize * (1 - percent)); //起始点,y值为水流的高度

//绘制多段波浪

for (int i = 0; i < waveCount * 2; i++) {

wavePath.rQuadTo(waveWidth / 2, waveHeight, waveWidth, 0);

wavePath.rQuadTo(waveWidth / 2, -waveHeight, waveWidth, 0);

}

wavePath.lineTo(rectSize, rectSize);

wavePath.lineTo(0, rectSize);

wavePath.close();

return wavePath;

}

所以实现波浪平移,实际方式是这样的:

83d929250d0e

实现水面上涨效果,就是属性动画实时修改起点的高度。

public void setProgressAnim(int num) {

ValueAnimator valueAnimator = ValueAnimator.ofInt(0, num);

valueAnimator.setDuration(ANIM_DURATION);

valueAnimator.setInterpolator(new DecelerateInterpolator());

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

curNum = (int) animation.getAnimatedValue();

percent = (float) curNum / totalNum; //实时更新进度状态

}

});

valueAnimator.start();

}

实现平移动画,用属性动画实时修改起点的水平位置,从-rectSize到0周期循环,rectSize为view的宽度。

private void setWaveMoveAnim() {

ValueAnimator valueAnimator = ValueAnimator.ofInt(100, 0);

valueAnimator.setDuration(ANIM_DURATION);

valueAnimator.setInterpolator(new LinearInterpolator());

valueAnimator.setRepeatCount(ValueAnimator.INFINITE); //无限循环

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

movePercent = (int) animation.getAnimatedValue();

moveDistance = (float) movePercent / 100 * rectSize;

postInvalidate();

}

});

valueAnimator.start();

}

绘制圆内文字以及设置到准确位置。

private void drawContentText(Canvas canvas) {

Rect rect = new Rect();

String content;

textPaint.setTextSize(160);

content = num + "M";

textPaint.getTextBounds(content, 0, content.length(), rect);

int numHeight = rect.height();

canvas.drawText(content, getWidth() / 2 - rect.width() / 2, getHeight() / 2 + numHeight / 2, textPaint);

textPaint.setTextSize(60);

content = "剩余流量";

textPaint.getTextBounds(content, 0, content.length(), rect);

canvas.drawText(content, getWidth() / 2 - rect.width() / 2, getHeight() / 2 - numHeight, textPaint);

textPaint.setTextSize(55);

content = "共" + (float) totalNum / 1024 + "GB";

textPaint.getTextBounds(content, 0, content.length(), rect);

canvas.drawText(content, getWidth() / 2 - rect.width() / 2, getHeight() / 2 + numHeight + dp2px(getContext(), 25), textPaint);

}

缓存bitmap,将两个canvas重叠,选取显示前景形状,背景颜色的模式。这里使用paint设置PorterDuffXfermode的Mode.SRC_IN模式。

private void drawBitmap(Canvas canvas) {

bitmap = Bitmap.createBitmap(rectSize, rectSize, Bitmap.Config.ARGB_8888);

bitmapCanvas = new Canvas(bitmap);

bitmapCanvas.drawCircle(rectSize / 2, rectSize / 2, rectSize / 2 - ringWidth, circlePaint);

bitmapCanvas.drawPath(setWavePath(), wavePaint);

canvas.drawBitmap(bitmap, 0, 0, null);

}

总共16中重叠显示模式设置,更详细的用法,可搜索"setXfermode"。

83d929250d0e

WaveProgressView类完整代码:

/**

* create by libo

* create on 2020/7/28

* description

*/

public class WaveProgressView extends View {

/**

* 波浪paint

*/

private Paint wavePaint;

/**

* 圆环paint

*/

private Paint circlePaint;

/**

* 圆环paint

*/

private Paint ringPaint;

/**

* 文本paint

*/

private Paint textPaint;

private Path wavePath;

private int waveWidth;

/**

* 波浪幅度

*/

private float waveHeight;

/**

* 水面波浪数,在为偶数的情况下,波浪才会按周期平移

*/

private final int waveCount = 2;

/**

* 当前数值

*/

private int curNum;

/**

* 总数值

*/

private int totalNum;

/**

* 最终进度比例

*/

private float percent;

/**

* 当前轮水波移动比例

*/

private int movePercent;

/**

* 该view尺寸大小

*/

private int rectSize;

/** 动画时长 */

private final int ANIM_DURATION = 2000;

private float moveDistance;

/**

* 缓存bitmap

*/

private Bitmap bitmap;

private Canvas bitmapCanvas;

private int ringWidth;

/**

* 当前量

*/

private int num;

public WaveProgressView(Context context) {

super(context);

init();

}

public WaveProgressView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

init();

}

private void init() {

wavePaint = new Paint();

wavePaint.setColor(getResources().getColor(R.color.mask_green));

wavePaint.setStyle(Paint.Style.FILL);

wavePaint.setAntiAlias(true);

wavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

circlePaint = new Paint();

circlePaint.setAntiAlias(true);

circlePaint.setColor(getResources().getColor(R.color.green));

ringPaint = new Paint();

ringPaint.setAntiAlias(true);

ringPaint.setColor(getResources().getColor(R.color.deep_green));

ringPaint.setStyle(Paint.Style.STROKE);

ringWidth = dp2px(getContext(), 12);

ringPaint.setStrokeWidth(ringWidth);

textPaint = new Paint();

textPaint.setColor(getResources().getColor(R.color.white));

textPaint.setAntiAlias(true);

wavePath = new Path();

setValue(634, 1024); //设置当前流量和总流量

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

waveWidth = getMeasuredWidth() / waveCount;

waveHeight = dp2px(getContext(), 18);

rectSize = getMeasuredWidth();

moveDistance = rectSize;

setMeasuredDimension(rectSize, rectSize); //宽高大小一致

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

drawBitmap(canvas);

//绘制外圆环

RectF rectF = new RectF(ringWidth / 2, ringWidth / 2, rectSize - ringWidth / 2, rectSize - ringWidth / 2);

canvas.drawArc(rectF, 0, 360, false, ringPaint);

drawContentText(canvas);

}

private Path setWavePath() {

wavePath.reset();

wavePath.moveTo(-moveDistance, rectSize * (1 - percent)); //起始点,y值为水流的高度

//绘制多段波浪

for (int i = 0; i < waveCount * 2; i++) {

wavePath.rQuadTo(waveWidth / 2, waveHeight, waveWidth, 0);

wavePath.rQuadTo(waveWidth / 2, -waveHeight, waveWidth, 0);

}

wavePath.lineTo(rectSize, rectSize);

wavePath.lineTo(0, rectSize);

wavePath.close();

return wavePath;

}

/**

* 缓存bitmap,将两个canvas重叠,选取显示前景形状,背景颜色的模式

*/

private void drawBitmap(Canvas canvas) {

bitmap = Bitmap.createBitmap(rectSize, rectSize, Bitmap.Config.ARGB_8888);

bitmapCanvas = new Canvas(bitmap);

bitmapCanvas.drawCircle(rectSize / 2, rectSize / 2, rectSize / 2 - ringWidth, circlePaint);

bitmapCanvas.drawPath(setWavePath(), wavePaint);

canvas.drawBitmap(bitmap, 0, 0, null);

}

/**

* 设置当前值和总值,并开启动画

*

* @param curNum

* @param totalNum

*/

public void setValue(int curNum, int totalNum) {

this.totalNum = totalNum;

setProgressAnim(curNum);

setWaveMoveAnim();

runWithAnimation(curNum);

}

/**

* 水波上涨动画

*/

public void setProgressAnim(int num) {

ValueAnimator valueAnimator = ValueAnimator.ofInt(0, num);

valueAnimator.setDuration(ANIM_DURATION);

valueAnimator.setInterpolator(new DecelerateInterpolator());

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

curNum = (int) animation.getAnimatedValue();

percent = (float) curNum / totalNum; //实时更新进度状态

}

});

valueAnimator.start();

}

/**

* 波浪平移动画

*/

private void setWaveMoveAnim() {

ValueAnimator valueAnimator = ValueAnimator.ofInt(100, 0);

valueAnimator.setDuration(ANIM_DURATION);

valueAnimator.setInterpolator(new LinearInterpolator());

valueAnimator.setRepeatCount(ValueAnimator.INFINITE); //无限循环

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

movePercent = (int) animation.getAnimatedValue();

moveDistance = (float) movePercent / 100 * rectSize;

postInvalidate();

}

});

valueAnimator.start();

}

/**

* 设置文字滚动动画

*

* @param number

*/

public void runWithAnimation(int number) {

ObjectAnimator objectAnimator = ObjectAnimator.ofInt(this, "num", 0, number);

objectAnimator.setDuration(ANIM_DURATION/2);

objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());

objectAnimator.start();

objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

postInvalidate();

}

});

}

/**

* 绘制圆内文字以及设置到准确位置

*/

private void drawContentText(Canvas canvas) {

Rect rect = new Rect();

String content;

textPaint.setTextSize(160);

content = num + "M";

textPaint.getTextBounds(content, 0, content.length(), rect);

int numHeight = rect.height();

canvas.drawText(content, getWidth() / 2 - rect.width() / 2, getHeight() / 2 + numHeight / 2, textPaint);

textPaint.setTextSize(60);

content = "剩余流量";

textPaint.getTextBounds(content, 0, content.length(), rect);

canvas.drawText(content, getWidth() / 2 - rect.width() / 2, getHeight() / 2 - numHeight, textPaint);

textPaint.setTextSize(55);

content = "共" + (float) totalNum / 1024 + "GB";

textPaint.getTextBounds(content, 0, content.length(), rect);

canvas.drawText(content, getWidth() / 2 - rect.width() / 2, getHeight() / 2 + numHeight + dp2px(getContext(), 25), textPaint);

}

public int getNum() {

return num;

}

public void setNum(int num) {

this.num = num;

}

private int dp2px(Context context, float dp) {

final float scale = context.getResources().getDisplayMetrics().density;

return (int) (dp * scale + 0.5f);

}

}

打完收工,该view还可做扩展的地方:随着波浪升高,波幅可以从大到小;可以实现双波浪效果。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值