今天我们要实现的这个vIEw没有太多交互性的vIEw,所以就继承vIEw。
自定义view的套路,套路很深
1、获取我们自定义属性attrs(可省略)
2、重写onMeasure方法,计算控件的宽和高
3、重写onDraw方法,绘制我们的控件
这么看来,自定义view的套路很清晰嘛。
我们看下今天的效果图,其中一个是放慢的效果(时间调的长)
我们按照套路来。
一.自定义属性
<declare-styleable name="WaveProgressVIEw"> <attr name="radius" format="dimension|reference" /> <attr name="radius_color" format="color|reference" /> <attr name="progress_text_color" format="color|reference" /> <attr name="progress_text_size" format="dimension|reference" /> <attr name="progress_color" format="color|reference" /> <attr name="progress" format="float" /> <attr name="maxProgress" format="float" /> </declare-styleable>
看下效果图我们就知道因该需要哪些属性。就不说了。
然后就是获取我们的这些属性,就是用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是控制点.利用塞尔曲线的公式就可以得道沿途的一些点,最后把点连起来就是喽。
下面这个图片来于网络:
二阶贝塞尔曲线
在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
喽。
大家可以结合下面这个图来理解下上面的话。
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能有所帮助。
总结以上是内存溢出为你收集整理的Android自定义view实现水波纹进度球效果全部内容,希望文章能够帮你解决Android自定义view实现水波纹进度球效果所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)