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://xiahunao.cn/news/2905343.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Stable Diffusion WebUI 生成参数:脚本(Script)——提示词矩阵、从文本框或文件载入提示词、X/Y/Z图表

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 大家好,我是水滴~~ 在本篇文章中,我们将深入探讨 Stable Diffusion WebUI 的另一个引人注目的生成参数——脚本(Script)。我们将逐一细说提示词矩阵、从文本框或文件导入提示词,…

2.4 比较检验 机器学习

目录 常见比较检验方法 总述 2.4.1 假设检验 2.4.2 交叉验证T检验 2.4.3 McNemar 检验 接我们的上一篇《性能度量》&#xff0c;那么我们在某种度量下取得评估结果后&#xff0c;是否可以直接比较以评判优劣呢&#xff1f;实际上是不可以的。因为我们第一&#xff0c;测试…

iOS UIFont-实现三方字体的下载和使用

UIFont 系列传送门 第一弹加载本地字体:iOS UIFont-新增第三方字体 第二弹加载线上字体:iOS UIFont-实现三方字体的下载和使用 前言 在上一章我们完成啦如何加载使用本地的字体。如果我们有很多的字体可供用户选择,我们当然可以全部使用本地字体加载方式,可是这样就增加了…

【Golang入门教程】Go语言变量的初始化

文章目录 强烈推荐引言举例多个变量同时赋值总结强烈推荐专栏集锦写在最后 强烈推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站:人工智能 推荐一个个人工作&#xff0c;日常中比较常…

使用 Yoda 和 ClickHouse 进行实时欺诈检测

背景 Instacart 是北美领先的在线杂货公司,拥有数百万活跃的客户和购物者。在其平台上打击欺诈和滥用行为不仅对于维护一个值得信赖和安全的环境至关重要,也对保持Instacart的财务健康至关重要。在这篇文章中,将介绍了一个欺诈平台——Yoda,解释了为什么我们选择ClickHous…

MyEclipse打开文件跳转到notepad打开问题

问题描述 windows系统打开README.md文件&#xff0c;每次都需要右键选择notepad打开&#xff0c;感觉很麻烦&#xff0c;然后就把README.md文件打开方式默认选择了notepad&#xff0c;这样每次双击就能打开&#xff0c;感觉很方便。 然后某天使用MyEclipse时&#xff0c;双击RE…

基于SpringBoot+VUE的后台资金管理系统

采用技术 基于SpringBootVUE的后台资金管理系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 员工首页 采购申请 商品添加 数据查询 管理员首页 …

数字化驱动乡村发展:数字乡村助力农村繁荣

随着信息技术的迅猛发展&#xff0c;数字化已成为驱动社会进步的重要引擎。在乡村发展的道路上&#xff0c;数字乡村以其独特的魅力&#xff0c;正在成为推动农村繁荣的重要力量。数字化技术的应用不仅为乡村带来了便捷和高效&#xff0c;更为乡村的经济、社会、文化等多个方面…

mysql 常见运算符

学习了mysql数据类型&#xff0c;接下来学习mysql常见运算符。 2&#xff0c;常见运算符介绍 运算符连接表达式中各个操作数&#xff0c;其作用是用来指明对操作数所进行的运算。运用运算符 可以更加灵活地使用表中的数据&#xff0c;常见的运算符类型有&#xff1a;算…

Day46:WEB攻防-注入工具SQLMAPTamper编写指纹修改高权限操作目录架构

目录 数据猜解-库表列数据&字典 权限操作-文件&命令&交互式 提交方法-POST&HEAD&JSON 绕过模块-Tamper脚本-使用&开发 分析拓展-代理&调试&指纹&风险&等级 知识点&#xff1a; 1、注入工具-SQLMAP-常规猜解&字典配置 2、注入…

Ubuntu下使用vscode进行C/C++开发:进阶篇

在vscode上进行C/C++开发的进阶需求: 1) 编写及调试源码时,可进行断点调试、可跨文件及文件夹进行函数调用。 2) 可生成库及自动提取对应的头文件和库文件。 3) 可基于当前工程资源一键点击验证所提取的库文件的正确性。 4) 可结合find_package实现方便的调用。 对于第一…

重写、重定义(隐藏)、重载区别

1、重载是在同一个作用域中比如在同一个类中、函数名一样参数不同 2、重写&#xff1a; 满足多态的条件&#xff1a;&#xff08;1&#xff09;虚函数前面带有virtual函数名、返回值、参数相同&#xff08;2&#xff09;重写函数体 3、重定义也叫隐藏、不满足重写的就是重定义

发票是扫码验真好,还是OCR后进行验真好?

随着科技的进步&#xff0c;电子发票的普及使得发票的验真方式也在不断演进。目前&#xff0c;我们常见的发票验真方式主要有两种&#xff1a;一种是扫描发票上的二维码进行验真&#xff0c;另一种是通过OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别…

ssh 公私钥(github)

一、生成ssh公私钥 生成自定义名称的SSH公钥和私钥对&#xff0c;需要使用ssh-keygen命令&#xff0c;这是大多数Linux和Unix系统自带的标准工具。下面&#xff0c;简单展示如何使用ssh-keygen命令来生成具有自定义名称的SSH密钥对。 步骤 1: 打开终端 首先&#xff0c;打开我…

mysql--事务四大特性与隔离级别

事务四大特性与隔离级别 mysql事务的概念事务的属性事务控制语句转账示例 并发事务引发的问题脏读脏读场景 不可重复读幻读幻读场景 事务的隔离级别读未提交读已提交可重复读&#xff08;MySQL默认&#xff09; 总结 mysql事务的概念 事务就是一组操作的集合&#xff0c;他是一…

centos node puppeteer chrome报错问题

原因&#xff1a;缺少谷歌依赖包&#xff0c;安装以下即可 yum install atkyum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf…

Selenium 自动化 —— 切换浏览器窗口

更多内容请关注我的 Selenium 自动化 专栏&#xff1a; 入门和 Hello World 实例使用WebDriverManager自动下载驱动Selenium IDE录制、回放、导出Java源码浏览器窗口操作 平时我们在使用浏览器时&#xff0c;通常会打开多个窗口&#xff0c;然后再多个窗口中来回切换&#xf…

Qt扫盲-QAssisant 集成其他qch帮助文档

QAssisant 集成其他qch帮助文档 一、概述二、Cmake qch例子1. 下载 Cmake.qch2. 添加qch1. 直接放置于Qt 帮助的目录下2. 在 QAssisant中添加 一、概述 QAssisant是一个很好的帮助文档&#xff0c;他提供了供我们在外部添加新的 qch帮助文档的功能接口&#xff0c;一般有两中添…

【人工智能Ⅱ】实验4:Unet眼底血管图像分割

实验4&#xff1a;Unet眼底血管图像分割 一&#xff1a;实验目的与要求 1&#xff1a;掌握图像分割的含义。 2&#xff1a;掌握利用Unet建立训练模型。 3&#xff1a;掌握使用Unet进行眼底血管图像数据集的分割。 二&#xff1a;实验内容 1&#xff1a;用Unet网络完成眼底血…

消失的两年

近两年 现在是2024年3月28号下午2点59分&#xff0c;此刻的我终于有了时间对于我过去的两年进行总结。 我于2020年本科毕业&#xff0c;带着对于未来的无限憧憬选择了自主创业。在21年小结中我曾提到过&#xff0c;自己耗费半年做的微信小程序并不被团队看重的时候&#xff0…