android 贝塞尔曲线实现进度球,Android自定义view实现水波纹进度球效果

今天我们要实现的这个view没有太多交互性的view,所以就继承view。

自定义view的套路,套路很深

1、获取我们自定义属性attrs(可省略)

2、重写onMeasure方法,计算控件的宽和高

3、重写onDraw方法,绘制我们的控件

这么看来,自定义view的套路很清晰嘛。

我们看下今天的效果图,其中一个是放慢的效果(时间调的长)

15059123501.gif?2016719161053

15059123512.gif?2016719161137

我们按照套路来。

看下效果图我们就知道因该需要哪些属性。就不说了。

然后就是获取我们的这些属性,就是用TypedArray来获取。当然是在构造中获取,一般我们会复写构造方法,少参数调用参数多的,然后走到参数最多的那个。

TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.WaveProgressView,defStyleAttr,R.style.WaveProgressViewDefault);

radius = (int) a.getDimension(R.styleable.WaveProgressView_radius,radius);

textColor = a.getColor(R.styleable.WaveProgressView_progress_text_color,0);

textSize = a.getDimensionPixelSize(R.styleable.WaveProgressView_progress_text_size,0);

progressColor = a.getColor(R.styleable.WaveProgressView_progress_color,0);

radiusColor = a.getColor(R.styleable.WaveProgressView_radius_color,0);

progress = a.getFloat(R.styleable.WaveProgressView_progress,0);

maxProgress = a.getFloat(R.styleable.WaveProgressView_maxProgress,100);

a.recycle();

注: R.style.WaveProgressViewDefault是这个控件的默认样式。

二.onMeasure测量

我们重写这个方法主要是更具父看见的宽和高来设置自己的宽和高。

@Override

protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {

//计算宽和高

int exceptW = getPaddingLeft() + getPaddingRight() + 2 * radius;

int exceptH = getPaddingTop() + getPaddingBottom() + 2 * radius;

int width = resolveSize(exceptW,widthMeasureSpec);

int height = resolveSize(exceptH,heightMeasureSpec);

int min = Math.min(width,height);

this.width = this.height = min;

//计算半径,减去padding的最小值

int minLR = Math.min(getPaddingLeft(),getPaddingRight());

int minTB = Math.min(getPaddingTop(),getPaddingBottom());

minPadding = Math.min(minLR,minTB);

radius = (min - minPadding * 2) / 2;

setMeasuredDimension(min,min);

}

首先该控件的宽和高肯定是一样的,因为是个圆嘛。其实是宽和高与半径和内边距有关,这里的内边距,我们取上下左右最小的一个。宽和高也选择取最小的。

this.width = this.height = min;包含左右边距。

resolveSize这个方法很好的为我们实现了我们想要的宽和高我慢看下源码。

public static int resolveSizeAndState(int size,int measureSpec,int childMeasuredState) {

final int specMode = MeasureSpec.getMode(measureSpec);

final int specSize = MeasureSpec.getSize(measureSpec);

final int result;

switch (specMode) {

case MeasureSpec.AT_MOST:

if (specSize < size) {

result = specSize | MEASURED_STATE_TOO_SMALL;

} else {

result = size;

}

break;

case MeasureSpec.EXACTLY:

result = specSize;

break;

case MeasureSpec.UNSPECIFIED:

default:

result = size;

}

return result | (childMeasuredState & MEASURED_STATE_MASK);

}

如果我们自己写也是这样写。

最后通过setMeasuredDimension设置宽和高。

三.onDraw绘制

关于绘制有很多android 提供了很多API,这里就不多说了。

绘制首先就是一些画笔的初始化。

需要提一下绘制path路径的画笔设置为PorterDuff.Mode.SRC_IN模式,这个模式只显示重叠的部分。

pathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

pathPaint.setColor(progressColor);

pathPaint.setDither(true);

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

我们要将所有的绘制 绘制到一个透明的bitmap上,然后将这个bitmap绘制到canvas上。

if (bitmap == null) {

bitmap = Bitmap.createBitmap(this.width,this.height,Bitmap.Config.ARGB_8888);

bitmapCanvas = new Canvas(bitmap);

}

为了方便计算和绘制,我将坐标系平移padding的距离

bitmapCanvas.save();

//移动坐标系

bitmapCanvas.translate(minPadding,minPadding);

// .... some thing

bitmapCanvas.restore();

3.1绘制圆

bitmapCanvas.drawCircle(radius,radius,circlePaint);

3.2绘制PATH 路径.

一是要实现波纹的左右飘,和上下的振幅慢慢的减小

绘制这个之前我们需要知道二阶贝塞尔曲线的大致原理。

简单的说就是知道:P1起始点,P2是终点,P1是控制点.利用塞尔曲线的公式就可以得道沿途的一些点,最后把点连起来就是喽。

下面这个图片来于网络:

15059123523.gif?201671916155

二阶贝塞尔曲线

在android-sdk里提供了绘制贝塞尔曲线的函数rQuadTo方法

public void rQuadTo(float dx1,float dy1,float dx2,float dy2)

dx1:控制点X坐标,表示相对上一个终点X坐标的位移坐标,可为负值,正值表示相加,负值表示相减;

dy1:控制点Y坐标,相对上一个终点Y坐标的位移坐标。同样可为负值,正值表示相加,负值表示相减;

dx2:终点X坐标,同样是一个相对坐标,相对上一个终点X坐标的位移值,可为负值,正值表示相加,负值表示相减;

dy2:终点Y坐标,同样是一个相对,相对上一个终点Y坐标的位移值。可为负值,正值表示相加,负值表示相减;

这四个参数都是传递的都是相对值,相对上一个终点的位移值。

要实现振幅慢慢的减小我们可以调节控制点的y坐标即可,即:

float percent=progress * 1.0f / maxProgress;

就可以得到[0,1]的

一个闭区间,[0,1]这货好啊,我喜欢,可以来做很多事情。

这样我们就可以根据percent来调节控制点的y坐标了。

//根据直径计算绘制贝赛尔曲线的次数

int count = radius * 4 / 60;

//控制-控制点y的坐标

float point = (1 - percent) * 15;

for (int i = 0; i < count; i++) {

path.rQuadTo(15,-point,30,0);

path.rQuadTo(15,point,0);

}

要实现左右波纹只需要控制闭合路径的左上角的x坐标即可,当然也是根据percent喽。

大家可以结合下面这个图来理解下上面的话。

15059123524.png?201671916183

path绘制的完整代码片段。

//绘制PATH

//重置绘制路线

path.reset();

float percent=progress * 1.0f / maxProgress;

float y = (1 - percent) * radius * 2;

//移动到右上边

path.moveTo(radius * 2,y);

//移动到最右下方

path.lineTo(radius * 2,radius * 2);

//移动到最左下边

path.lineTo(0,radius * 2);

//移动到左上边

// path.lineTo(0,y);

//实现左右波动,根据progress来平移

path.lineTo(-(1 -percent) * radius*2,y);

if (progress != 0.0f) {

//根据直径计算绘制贝赛尔曲线的次数

int count = radius * 4 / 60;

//控制-控制点y的坐标

float point = (1 - percent) * 15;

for (int i = 0; i < count; i++) {

path.rQuadTo(15,0);

}

}

//闭合

path.close();

bitmapCanvas.drawPath(path,pathPaint);

3.3绘制进度的文字

这个就比较简单了,绘制在控件的中间即可。关于文字的坐标计算还是很好理解的。

//绘制文字

String text = progress + "%";

float textW = textPaint.measureText(text);

Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();

float baseLine = radius - (fontMetrics.ascent + fontMetrics.descent) / 2;

bitmapCanvas.drawText(text,radius - textW / 2,baseLine,textPaint);

最后别忘了把我们的bitmap绘制到canvas上。

canvas.drawBitmap(bitmap,null);

哦,最后是实用方法,这里我们不用thread+handler,我们用属性动画。

你懂的!!!,like

ObjectAnimator objectAnimator0 = ObjectAnimator.ofFloat(waveProgressView_0,"progress",0f,100f);

objectAnimator0.setDuration(3300);

objectAnimator0.setInterpolator(new LinearInterpolator());

objectAnimator0.start();

结束语

至此,也就实现了我们的效果。以上就是本文的全部内容,希望本文的内容对大家开发Android能有所帮助。

总结

如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。

小编个人微信号 jb51ccc

喜欢与人分享编程技术与工作经验,欢迎加入编程之家官方交流群!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值