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);
    }

    @Override
    protected 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());
    }

    @Override
    protected 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;
    }

    @Override
    protected 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;
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity | 射线检测及EventSystem总结

目录 一、知识概述 1.Input.mousePosition 2.Camera.ScreenToWorldPoint 3.Camera.ScreenPointToRay 4.Physics2D.Raycast 二、射线相关 1.3D&#xff08;包括UI&#xff09;、射线与ScreenPointToRay 2.3D&#xff08;包括UI&#xff09;、射线与ScreenToWorldPoint …

计算机基础,挑战全网最全解析

1.什么是计算机&#xff1f; 2.冯诺依曼结构 3.进制 4.摩尔斯码和布莱叶盲文 摩尔斯码 布莱叶盲文

如何使用群晖WebDAV实现固定公网地址同步Zotero文献管理器

文章目录 前言1. Docker 部署 Trfɪk2. 本地访问traefik测试3. Linux 安装cpolar4. 配置Traefik公网访问地址5. 公网远程访问Traefik6. 固定Traefik公网地址 前言 Trfɪk 是一个云原生的新型的 HTTP 反向代理、负载均衡软件&#xff0c;能轻易的部署微服务。它支持多种后端 (D…

蓝桥杯嵌入式学习笔记(6):IIC程序设计

目录 前言 1. IIC基本原理 2. 电路原理 3. 代码编程 3.1 预备工作 3.2 AT24C02写读功能编写 3.2.1 AT24C02写操作实现 3.2.2 AT24C02读操作实现 3.3 MCP4017写读功能编写 3.3.1 MCP4017写操作实现 3.3.2 MCP4017读操作实现 3.4 main.c编写 3.4.1 头文件引用 3.4.…

基于javaweb(springboot+mybatis)网上酒类商城项目设计和实现以及文档报告

基于javaweb(springbootmybatis)网上酒类商城项目设计和实现以及文档报告 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞…

Redis数据类型介绍和使用

数据类型 String&#xff08;字符串&#xff09;&#xff1a;最基本的数据类型&#xff0c;可以存储任何类型的数据&#xff0c;如文本、数字等。Hash&#xff08;哈希&#xff09;&#xff1a;用于存储字段-值对的散列集合&#xff0c;适用于存储对象。List&#xff08;列表&…

鱼哥赠书活动第14期:看完这本《数字化运维》掌握数字化运维方法,构建数字化运维体系

鱼哥赠书活动第14期&#xff1a;看完这本《数字化运维》掌握数字化运维方法&#xff0c;构建数字化运维体系 主要内容&#xff1a;读者对象&#xff1a;赠书抽奖规则:往期赠书福利&#xff1a; 数字化转型已经成为大势所趋&#xff0c;各行各业正朝着数字化方向转型&#xff0c…

如何在群晖NAS搭建bitwarden密码管理软件并实现无公网IP远程访问

前言 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊如何在群晖NAS搭建bitwarden密码管理软件并实现无公网IP远程访问&#xff0c;希望大家能觉得实用&#xff01; 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&am…

【数据结构】链表习题之环形链表的约瑟夫问题

&#x1f451;个人主页&#xff1a;啊Q闻 &#x1f387;收录专栏&#xff1a;《数据结构》 &#x1f389;道阻且长&#xff0c;行则将至 前言 今天这道题目时牛客上的题目&#xff0c;名为环形链表的约瑟夫问题&#xff0c;很有趣的的一道题目 环形链表的约瑟…

申请免费域名证书

目录 背景&#xff1a; 域名证书是什么&#xff1a; 域名证书有哪些&#xff1a; 部署域名证书有什么用&#xff1a; 免费的域名证书在哪里申请&#xff1a; 背景&#xff1a; 域名是一个IP地址上的“面具” 。一个域名的目的是便于记忆和沟通的一组服务器的地址(网站&…

OpenHarmony开发知识点记录之ABI

OpenHarmony系统支持丰富的设备形态&#xff0c;支持多种架构指令集&#xff0c;支持多种操作系统内核&#xff1b;为了应用在各种OpenHarmony设备上的兼容性&#xff0c;本文定义了"OHOS" ABI&#xff08;Application Binary Interface&#xff09;的基础标准&#…

路由协议RIP(悄悄话)

实验要求&#xff1a;总部和两个分支&#xff0c;拓扑如下图&#xff0c;利用rip路由协议使得各个pc设备可以通信 RIP理解&#xff1a;相邻路由定期交换内部路由协议&#xff0c;最后达到稳定状态&#xff0c;如果发生网络发生变化&#xff0c;重复交换路由步骤直到稳定状态&a…

LinkedIn 互联网架构扩展简史

LinkedIn成立于 2003 年&#xff0c;其目标是连接到您的网络以获得更好的工作机会。第一周只有 2,700 名会员。时间快进了很多年&#xff0c;LinkedIn 的产品组合、会员基础和服务器负载都取得了巨大的增长。 如今&#xff0c;LinkedIn 在全球运营&#xff0c;拥有超过 3.5 亿会…

研华工控机610L学习笔记2:visualstudio与第一个C#程序

今日继续学习工控机 C# 编程相关知识&#xff1a; 这篇结束后我将先进行一段时间的C#的学习研究&#xff0c;并写一些C#的笔记 后续再更新工控机编程设计相关 目录 1、安装visualstudio&#xff1a; 2、创建第一个C#程序&#xff1a; 3、寻找C#解决方案源文件&#xff1a; …

梦幻西游端游全新升级瀚海游戏玩法 一单35 小白一手机没脑子实际操作 日入3000

大家好&#xff0c;很多人都听过抖音游戏外国投资者方案&#xff0c;但是大部分人做视频&#xff0c;收益都非常低。 今天给带来的项目是“梦幻西游端游全新升级瀚海游戏玩法&#xff0c;一单35&#xff0c;轻松日入3000”&#xff0c;这个项目不用去被割韭菜&#xff0c;我自…

IDEA | 资源文件中文乱码问题解决

问题 IDEA打开资源文件&#xff0c;显示乱码问题。 解决方案 1、电脑是mac&#xff0c;点击IDEA->【Preferences】->【Editor】->【File Encodings】 2、选择【Properties Files】中的UTF-8&#xff0c;并勾选Transparent native-to-ascii conversion。 3、最后点击…

P5727 【深基5.例3】冰雹猜想

【深基5.例3】冰雹猜想 - 洛谷https://www.luogu.com.cn/problem/P5727这种方法比较繁琐&#xff0c;预先定义固定的数组长度&#xff0c;很局限&#xff1a; public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.next…

图神经网络和图卷积网络

图神经网络要点 1. GNNs adopt a “graph-in, graph-out” architecture meaning that these model types accept a graph as input, with information loaded into its nodes, edges and global-context, and progressively transform these embeddings, without changing th…

黑马头条知识点总结

黑马头条知识点总结 文章目录 黑马头条知识点总结前言一、使用的所有技术栈二、初始化项目 2.1加密盐登录2.2网关2.3配置nginx三。文章通过freemarker生成html文件存入minio中四。内容安全阿里云接口5.使用延迟任务发布审核文章 4.9.3)redis分布式锁在工具类CacheService中添加…

HWOD:名字的漂亮度

一、题目 描述 给出一个字符串&#xff0c;该字符串仅由小写字母组成&#xff0c;定义这个字符串的漂亮度是其所有字母漂亮度的总和 每个字母都有一个漂亮度&#xff0c;范围在1到26之间。没有任何两个不同字母拥有相同的漂亮度。字母忽略大小写。 给出多个字符串&#xff…