Android笔记(三十):PorterDuffXfermode实现旋转进度View

背景

核心原理是使用PorterDuffXfermode + Path来绘制进度,并实现圆角

效果图

Android笔记(三十)效果演示

进度条绘制步骤

  1. 将ImageView矩形七个点的坐标存储起来(configNodes)
    他们对应着7个不同的刻度,每个刻度的值 i * (1000 / 8)
    在这里插入图片描述
  2. 配置开始的点(configStartPoint)
    先计算坐标偏移量,再判断当前进度在哪个刻度范围内,设置正确的开始坐标
  3. 配置路径(configPath)
    从中心点开始,第二个点为上一步配置的开始点,后面根据进度progress和7个刻度点对应的刻度值进行比较,接着连线顶部中间点,最后回到中心点

圆角绘制原理

在这里插入图片描述
这里采用DST_OUT模式,DST是覆盖在ImageView上的半透明遮罩,SRC是动态绘制的白色进度条,取两者相交的区域并显示DST的像素,就能实现视频中的效果

完整代码

public class RingProgressView extends AppCompatImageView {/*** 每一个刻度为125,由1000/8获得*/private final static int PER_SCALE = 125;private final static float DEFAULT_RADIUS = 12f;private int progress;// 小于等于0或者大于等于100为消失private float perX, perY = 0f;private final PathNode startPoint = new PathNode();private final List<PathNode> nodes = new ArrayList<>();private boolean hasLoadNodes;private final Path path = new Path();private final Paint paintFill = new Paint(Paint.ANTI_ALIAS_FLAG);private static Bitmap bitmap = null;private boolean isDowning;private final PorterDuffXfermode porterDuffXfermode;private RectF rectF;private final float radius;public RingProgressView(Context context) {this(context, null);}public RingProgressView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public RingProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);paintFill.setStyle(Paint.Style.FILL);porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);radius = dp2px(context, DEFAULT_RADIUS);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);rectF = new RectF(0, 0, getWidth(), getHeight());}public static float dp2px(Context context, float dpi) {return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpi, context.getResources().getDisplayMetrics());}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (progress > 0) {if (perX == 0f) {perX = getWidth() / (2f * PER_SCALE);}if (perY == 0f) {perY = getHeight() / (2f * PER_SCALE);}configNodes();configStartPoint();configPath();int id = canvas.saveLayer(0, 0, getWidth(), getHeight(), paintFill, Canvas.ALL_SAVE_FLAG);paintFill.setColor(ContextCompat.getColor(getContext(), R.color.colorShadow));canvas.drawRoundRect(rectF, radius, radius, paintFill);paintFill.setXfermode(porterDuffXfermode);paintFill.setColor(Color.WHITE);canvas.drawPath(path, paintFill);paintFill.setXfermode(null);canvas.restoreToCount(id);}}/*** 统计所有节点*/private void configNodes() {if (!hasLoadNodes) {nodes.add(new PathNode(0, 0, 7 * PER_SCALE));nodes.add(new PathNode(0, getHeight() / 2f, 6 * PER_SCALE));nodes.add(new PathNode(0, getHeight(), 5 * PER_SCALE));nodes.add(new PathNode(getWidth() / 2f, getHeight(), 4 * PER_SCALE));nodes.add(new PathNode(getWidth(), getHeight(), 3 * PER_SCALE));nodes.add(new PathNode(getWidth(), getHeight() / 2f, 2 * PER_SCALE));nodes.add(new PathNode(getWidth(), 0, PER_SCALE));hasLoadNodes = true;}}/*** 配置第一个节点*/private void configStartPoint() {int pro = progress % PER_SCALE == 0 ? PER_SCALE : progress % PER_SCALE;float xPro = pro * perX;float yPro = pro * perY;if (progress <= PER_SCALE) {startPoint.setNode(getWidth() / 2f + xPro, 0, progress);} else if (progress <= 2 * PER_SCALE) {startPoint.setNode(getWidth(), yPro, progress);} else if (progress <= 3 * PER_SCALE) {startPoint.setNode(getWidth(), getHeight() / 2f + yPro, progress);} else if (progress <= 4 * PER_SCALE) {startPoint.setNode(getWidth() - xPro, getHeight(), progress);} else if (progress <= 5 * PER_SCALE) {startPoint.setNode(getWidth() / 2f - xPro, getHeight(), progress);} else if (progress <= 6 * PER_SCALE) {startPoint.setNode(0, getHeight() - yPro, progress);} else if (progress <= 7 * PER_SCALE) {startPoint.setNode(0, getHeight() / 2f - yPro, progress);} else if (progress < 8 * PER_SCALE) {startPoint.setNode(xPro, 0, progress);} else {progress = 0;invalidate();}}private void configPath() {path.reset();path.moveTo(getWidth() / 2f, getHeight() / 2f);path.lineTo(startPoint.x, startPoint.y);for (PathNode node : nodes) {if (node.weight < startPoint.weight) {path.lineTo(node.x, node.y);}}path.lineTo(getWidth() / 2f, 0);path.close();}/*** 设置进度 0-100** @param progress 这里乘以10,方便计算,因为1000除以8没有小数*/public void setProgress(int progress) {int temp = progress * 10;if (temp != this.progress) {this.progress = temp;invalidate();}}/*** 获取进度 0-100** @return 这里除以10,因为{@link RingProgressView#setProgress(int)}乘以10*/public int getProgress() {return progress / 10;}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();progress = 0;}/*** 储存Path需要走过的节点* weight代表权重,当大于进度progress时才加入Path*/private static class PathNode {private float x;private float y;private int weight;public PathNode() {}public PathNode(float x, float y, int weight) {this.x = x;this.y = y;this.weight = weight;}public void setNode(float x, float y, int weight) {this.x = x;this.y = y;this.weight = weight;}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/576562.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS入门--配置环境 + IDE汉化

文章目录 下载安装DevEco Studio配置环境先认识DevEco Studio界面工程目录工程级目录模块级目录 app.json5module.json5main_pages.json通知栏预览区 运行模拟器IED汉化 下载安装DevEco Studio 去官网下载DevEco Studio完了安装 配置环境 打开已安装的DevEco Studio快捷方式…

fastadmin学习04-一键crud

FastAdmin 默认内置一个 test 表&#xff0c;可根据表字段名、字段类型和字段注释通过一键 CRUD 自动生成。 create table fa_test (id int unsigned auto_increment comment ID primary key,user_id int(10) default 0 null…

C语言--编译和链接

1.翻译环境 计算机能够执行二进制指令&#xff0c;我们的电脑不会直接执行C语言代码&#xff0c;编译器把代码转换成二进制的指令&#xff1b; 我们在VS上面写下printf("hello world");这行代码的时候&#xff0c;经过翻译环境&#xff0c;生成可执行的exe文件&…

让IIS支持.NET Web Api PUT和DELETE请求

前言 有很长一段时间没有使用过IIS来托管应用了&#xff0c;今天用IIS来托管一个比较老的.NET Fx4.6的项目。发布到线上后居然一直调用不同本地却一直是正常的&#xff0c;关键是POST和GET请求都是正常的&#xff0c;只有PUT和DELETE请求是有问题的。经过一番思考忽然想起来了I…

关系型数据库mysql(8)sql高级语句②

目录 一.子查询——Subquery 语法 环境准备 In——查询已知的值的数据记录 子查询——Insert 子查询——Update 子查询——Delete Not In——表示否定&#xff0c;不在子查询的结果集里 Exists——判断查询结果集是否为空 子查询——别名 ​编辑 二.视图 理论&a…

2. Java基本语法

文章目录 2. Java基本语法2.1 关键字保留字2.1.1 关键字2.1.2 保留字2.1.3 标识符2.1.4 Java中的名称命名规范 2.2 变量2.2.1 分类2.2.2 整型变量2.2.3 浮点型2.2.4 字符型 char2.2.5 Unicode编码2.2.6 UTF-82.2.7 boolean类型 2.3 基本数据类型转换2.3.1 自动类型转换2.2.2 强…

大话设计模式之原型模式

原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;它用于创建对象的复制&#xff0c;同时又能保持对象的封装。原型模式通过复制现有对象的方式来创建新的对象&#xff0c;而无需知道具体创建过程的细节。 在原型模式中&#xff0c;通常会有…

吴恩达2022机器学习专项课程(一) 4.2 梯度下降实践

问题预览/关键词 本节内容梯度下降更新w的公式梯度下降更新b的公式的含义α的含义为什么要控制梯度下降的幅度&#xff1f;导数项的含义为什么要控制梯度下降的方向&#xff1f;梯度下降何时结束&#xff1f;梯度下降算法收敛的含义正确更新梯度下降的顺序错误更新梯度下降的顺…

激光焊接机在不锈钢三角阀制造中的应用与发展

不锈钢三角阀激光焊接机是一种专门用于焊接不锈钢三角阀的高效、精准设备。这种设备在不锈钢三角阀的制造过程中起到了至关重要的作用&#xff0c;其应用主要体现在以下几个方面&#xff1a; ​ 一、激光焊接机在不锈钢三角阀制造中的应用 激光焊接机以其独特的优势&#xff…

【JavaWeb】Day24.Web入门——SPringBootWeb入门

什么是SPring&#xff1f; 我们可以打开Spring的官网(Spring | Home)&#xff0c;去看一下Spring的简介&#xff1a;Spring makes Java simple。Spring的官方提供很多开源的项目&#xff0c;我们可以点击上面的projects&#xff0c;看到spring家族旗下的项目&#xff0c;按照流…

语义分割——交通事故数据集

一、重要性及意义 首先&#xff0c;语义分割可以帮助我们更深入地理解交通事故现场的情况。通过对事故现场图像或视频的精确分割&#xff0c;可以识别出道路、车辆、行人等不同的元素&#xff0c;并理解它们之间的关系。这有助于我们更准确地分析事故发生的原因和过程&#xf…

微服务demo(二)nacos服务注册与集中配置

环境&#xff1a;nacos1.3.0 一、服务注册 1、pom&#xff1a; 移步spring官网https://spring.io&#xff0c;查看集成Nacos所需依赖 找到对应版本点击进入查看集成说明 然后再里面找到集成配置样例&#xff0c;这里只截一张&#xff0c;其他集成内容继续向下找 我的&#x…