热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

在我们的日常开发中自定义控件还是用的挺多的,设计师或者产品为了更好的漂亮,美观,交互都会做一些牛逼的ui效果图,但是最后实现的还是我们程序员啊。所以说自定义view你还是得会的。

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

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

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

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

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

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

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


我们按照套路来。

一.自定义属性

 
  
  
  
  
  
  
  
 

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

然后就是获取我们的这些属性,就是用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 

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

最后通过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, 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 

要实现左右波纹只需要控制闭合路径的左上角的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 

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, 0, 0, null);

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

你懂的!!!,like

 ObjectAnimator objectAnimator0 = ObjectAnimator.ofFloat(waveProgressView_0, "progress", 0f, 100f);
  objectAnimator0.setDuration(3300);
  objectAnimator0.setInterpolator(new LinearInterpolator());
  objectAnimator0.start();

结束语

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


推荐阅读
  • 2023 ARM嵌入式系统全国技术巡讲旨在分享ARM公司在半导体知识产权(IP)领域的最新进展。作为全球领先的IP提供商,ARM在嵌入式处理器市场占据主导地位,其产品广泛应用于90%以上的嵌入式设备中。此次巡讲将邀请来自ARM、飞思卡尔以及华清远见教育集团的行业专家,共同探讨当前嵌入式系统的前沿技术和应用。 ... [详细]
  • Android 九宫格布局详解及实现:人人网应用示例
    本文深入探讨了人人网Android应用中独特的九宫格布局设计,解析其背后的GridView实现原理,并提供详细的代码示例。这种布局方式不仅美观大方,而且在现代Android应用中较为少见,值得开发者借鉴。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 本文总结了在使用Ionic 5进行Android平台APK打包时遇到的问题,特别是针对QRScanner插件的改造。通过详细分析和提供具体的解决方法,帮助开发者顺利打包并优化应用性能。 ... [详细]
  • 理解存储器的层次结构有助于程序员优化程序性能,通过合理安排数据在不同层级的存储位置,提升CPU的数据访问速度。本文详细探讨了静态随机访问存储器(SRAM)和动态随机访问存储器(DRAM)的工作原理及其应用场景,并介绍了存储器模块中的数据存取过程及局部性原理。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
author-avatar
布瓜Pourqu2502854853
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有