源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

Android自定义view实现水波纹进度球效果

  • 时间:2022-01-25 06:14 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:Android自定义view实现水波纹进度球效果
今天我们要实现的这个view没有太多交互性的view,所以就继承view。 [b]自定义view的套路,套路很深[/b]       1、获取我们自定义属性attrs(可省略)       2、重写onMeasure方法,计算控件的宽和高       3、重写onDraw方法,绘制我们的控件 这么看来,自定义view的套路很清晰嘛。 我们看下今天的效果图,其中一个是放慢的效果(时间调的长) [img]http://files.jb51.net/file_images/article/201608/2016819161038668.gif?2016719161053[/img] [img]http://files.jb51.net/file_images/article/201608/2016819161125751.gif?2016719161137[/img] [b]我们按照套路来。[/b] [b]一.自定义属性[/b]
 <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>
看下效果图我们就知道因该需要哪些属性。就不说了。 然后就是获取我们的这些属性,就是用[code]TypedArray[/code]来获取。当然是在构造中获取,一般我们会复写构造方法,少参数调用参数多的,然后走到参数最多的那个。
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是这个控件的默认样式。 [b]二.onMeasure测量[/b] 我们重写这个方法主要是更具父看见的宽和高来设置自己的宽和高。
 @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);
 }
首先该控件的宽和高肯定是一样的,因为是个圆嘛。其实是宽和高与半径和内边距有关,这里的内边距,我们取上下左右最小的一个。宽和高也选择取最小的。 [code]this.width = this.height = min; [/code]包含左右边距。 [code]resolveSize[/code]这个方法很好的为我们实现了我们想要的宽和高我慢看下源码。
 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);
 }
如果我们自己写也是这样写。 最后通过[code]setMeasuredDimension[/code]设置宽和高。 [b]三.onDraw绘制[/b] 关于绘制有很多android 提供了很多API,这里就不多说了。 绘制首先就是一些画笔的初始化。 需要提一下绘制path路径的画笔设置为[code]PorterDuff.Mode.SRC_IN[/code]模式,这个模式只显示重叠的部分。
 pathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  pathPaint.setColor(progressColor);
  pathPaint.setDither(true);
  pathPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
我们要将所有的绘制 绘制到一个透明的[code]bitmap[/code]上,然后将这个[code]bitmap[/code]绘制到canvas上。
if (bitmap == null) {
   bitmap = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888);
   bitmapCanvas = new Canvas(bitmap);
  }
为了方便计算和绘制,我将坐标系平移[code]padding[/code]的距离
 bitmapCanvas.save();
  //移动坐标系
  bitmapCanvas.translate(minPadding, minPadding);
 // .... some thing
 bitmapCanvas.restore();
[b]3.1绘制圆[/b]
  bitmapCanvas.drawCircle(radius, radius, radius, circlePaint);
[b]3.2绘制PATH 路径.[/b] 一是要实现波纹的左右飘,和上下的振幅慢慢的减小 绘制这个之前我们需要知道二阶贝塞尔曲线的大致原理。 简单的说就是知道:P1起始点,P2是终点,P1是控制点.利用塞尔曲线的公式就可以得道沿途的一些点,最后把点连起来就是喽。 下面这个图片来于网络: [img]http://files.jb51.net/file_images/article/201608/2016819161455299.gif?201671916155[/img] 二阶贝塞尔曲线 在android-sdk里提供了绘制贝塞尔曲线的函数[code]rQuadTo[/code]方法
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]这货好啊,我喜欢,可以来做很多事情。 这样我们就可以根据[code]percent[/code]来调节控制点的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, 30, 0);
   }
要实现左右波纹只需要控制闭合路径的左上角的x坐标即可,当然也是根据[code]percent[/code]喽。 大家可以结合下面这个图来理解下上面的话。 [img]http://files.jb51.net/file_images/article/201608/2016819161752272.png?201671916183[/img] [b]path绘制的完整代码片段。[/b]
 //绘制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, -point, 30, 0);
    path.rQuadTo(15, point, 30, 0);
   }
  }
  //闭合
  path.close();
  bitmapCanvas.drawPath(path, pathPaint);
[b]3.3绘制进度的文字[/b] 这个就比较简单了,绘制在控件的中间即可。关于文字的坐标计算还是很好理解的。
 //绘制文字
  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);
最后别忘了把我们的[code]bitmap[/code]绘制到canvas上。
canvas.drawBitmap(bitmap, 0, 0, null);
哦,最后是实用方法,这里我们不用thread+handler,我们用属性动画。 你懂的!!!,like
 ObjectAnimator objectAnimator0 = ObjectAnimator.ofFloat(waveProgressView_0, "progress", 0f, 100f);
  objectAnimator0.setDuration(3300);
  objectAnimator0.setInterpolator(new LinearInterpolator());
  objectAnimator0.start();
[b]结束语[/b] 至此,也就实现了我们的效果。以上就是本文的全部内容,希望本文的内容对大家开发Android能有所帮助。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部