[Android自定义View]实现一个环形进度条控件

[Android View]实现一个环形进度条

在这里插入图片描述

导言

之前的文章里我们已经介绍了自定义View相关的知识了,本篇文章我们就来实战一下,从零到一,实现一个环形进度条的控件。

具体实现

大体框架

我们说过,如果要实现一个自定义控件的话一般有两种继承方式:

  1. 继承View:重写onDraw,还需要支持warp_content等属性。
  2. 继承系统已有控件:重写onDraw 或者绘制中的其他方法,一般用于拓展已有控件的功能。

如果要实现环形进度条的话,目前应该是没有系统已有的控件可以拓展,都需要较大程度的改动,所以我们直接继承View来实现即可。其次我们再来梳理一下要实现环形进度条的几个关键点:

  1. 绘制环形(根据进度,显示的颜色)
  2. 支持warp_content属性和padding属性
    实际上也并不难,主要就是根据进度的不同绘制环形这一步,完成这一步,环形进度条也大致完毕。

确定尺寸

这里我们就先不考虑warp_content,只考虑padding这个特殊情况:

//考虑padding之后的尺寸边界
val mSizeWithPaddingget() = RectF(0f+paddingLeft,0f+paddingTop,width.toFloat()-paddingRight-paddingLeft,height.toFloat()-paddingBottom-paddingTop)
//绘制内容的宽度
private val contentWidthget() = width-paddingLeft-paddingRight
//绘制内容的高度
private val contentHeightget() = height-paddingTop-paddingBottom

绘制环形&文字

我们先来介绍最重要的一点:如何绘制环形。这一片绘制view的内容我强烈建议大家可以去学习朱凯老师(扔物线)的课程,基本上涵盖了我们常用的绘制内容。

这里来简单介绍一下使用到的,Canavs绘制相关的API:
在这里插入图片描述
要实现环形进度条我们用这两个绘制方法就可以了。

具体绘制

既然是进度条那么就应该有当前进度值和最大进度值,这两个进度只要是为了确定在绘制的时候我们需要绘制弧度为多少的圆弧,我们将最大进度值设置为100,用以下代码表示:

class CircleLineWithText @JvmOverloads constructor(mContext: Context, attributeSet: AttributeSet? = null, defStyle:Int = 0
):View(mContext,attributeSet,defStyle) { ....//更新进度fun updateProgress(progressIn100:Int) {val tragetRad = progressIn100 * 360 / 100currentRad = tragetRad.toFloat()invalidate()}
}

这里我定义了一个updateProgress方法来更新当前进度,我们都知道一圈圆为360度,所以说当前的目标弧度值为
(当前进度值 / 100) * 360 ,但是在整形中我们显然不能这么做,所以我们先乘以360再除以100;并在最后调用invalidate方法来触发View的重新绘制。
现在有了弧度值我们再来看具体的绘制方法(过程):

override fun onDraw(canvas: Canvas) {super.onDraw(canvas)//着色器 -- 辐射渐变val shader = SweepGradient((paddingLeft+contentWidth/2-paddingRight).toFloat(),(paddingTop+contentHeight/2-paddingBottom).toFloat(),mLineColor,Color.RED)mLinePaint.color = mLineColormLinePaint.shader = shader//测试用的数据Log.d(TAG, "onDraw: x:${contentWidth},y:${contentHeight},r:${min(contentWidth,contentHeight).toFloat()/2}")//根据目标弧度值绘制弧形canvas.drawArc(paddingLeft.toFloat()+mLinePaint.strokeWidth,paddingTop.toFloat()+mLinePaint.strokeWidth,paddingLeft+min(contentHeight,contentWidth).toFloat()-mLinePaint.strokeWidth-paddingRight,paddingTop+min(contentHeight,contentWidth).toFloat()-mLinePaint.strokeWidth-paddingBottom,0f,currentRad,false,mLinePaint)//绘制文字mTextPaint.color = mTextColormTextPaint.textSize = mTextSizemTextPaint.style = Paint.Style.FILLmTextPaint.isUnderlineText = true//需要显示在环形进度条中间的字符串contentString = (currentRad/360*100).toInt().toString() + "%"val lengthOfString = contentString.length * 25canvas.drawText(contentString,min(contentWidth,contentHeight).toFloat()/2-lengthOfString/2,min(contentWidth,contentHeight).toFloat()/2+lengthOfString/10,mTextPaint)mTextPaint.style = Paint.Style.STROKE//测试用if (Debug) {canvas.drawRect(mSizeWithPadding,mTextPaint)canvas.drawRect(paddingLeft.toFloat()+mLinePaint.strokeWidth,paddingTop.toFloat()+mLinePaint.strokeWidth,paddingLeft+min(contentHeight,contentWidth).toFloat()-mLinePaint.strokeWidth-paddingRight,paddingTop+min(contentHeight,contentWidth).toFloat()-mLinePaint.strokeWidth-paddingBottom,mTextPaint)}}

这段就是绘制环形进度条的核心代码,实际上逻辑非常简单,主要就是我们需要对Canavs相关的API有所了解,其中关于绘制的线条的颜色,我使用到了shade着色器的辐射渐变模式,这样绘制的线条颜色就会随着绘制的位置改变。

完整代码:

class CircleLineWithText @JvmOverloads constructor(mContext: Context, attributeSet: AttributeSet? = null, defStyle:Int = 0
):View(mContext,attributeSet,defStyle) {companion object {private const val TAG = "CircleLineWithText"private const val Debug = true}var contentString = "Enjoy Your Life Cmf"private val Color_TransParentget() = resources.getColor(android.R.color.transparent)var mTextColor = Color.BLACKvar mLineColor = Color.BLACKvar mTextSize = 18fvar currentRad = 0finit {//自定义属性val typeArray = mContext.obtainStyledAttributes(attributeSet, R.styleable.CircleLineWithText)mTextColor = typeArray.getColor(R.styleable.CircleLineWithText_text_Color,Color.BLACK)mLineColor = typeArray.getColor(R.styleable.CircleLineWithText_line_Color,Color.BLACK)mTextSize = typeArray.getFloat(R.styleable.CircleLineWithText_text_Size,18f)currentRad = typeArray.getFloat(R.styleable.CircleLineWithText_current_Radius,0f)}private val mLinePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {strokeCap = Paint.Cap.ROUNDstyle = Paint.Style.STROKEstrokeWidth = 30fcolor = Color_TransParent}private val mTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {color = Color_TransParentstyle = Paint.Style.STROKEstrokeWidth = 0f}val mSizeWithPaddingget() = RectF(0f+paddingLeft,0f+paddingTop,width.toFloat()-paddingRight-paddingLeft,height.toFloat()-paddingBottom-paddingTop)private val contentWidthget() = width-paddingLeft-paddingRightprivate val contentHeightget() = height-paddingTop-paddingBottomfun updateProgress(progressIn100:Int) {val tragetRad = progressIn100 * 360 / 100currentRad = tragetRad.toFloat()invalidate()}@SuppressLint("DrawAllocation")override fun onDraw(canvas: Canvas) {super.onDraw(canvas)val shader = SweepGradient((paddingLeft+contentWidth/2-paddingRight).toFloat(),(paddingTop+contentHeight/2-paddingBottom).toFloat(),mLineColor,Color.RED)mLinePaint.color = mLineColormLinePaint.shader = shaderLog.d(TAG, "onDraw: x:${contentWidth},y:${contentHeight},r:${min(contentWidth,contentHeight).toFloat()/2}")canvas.drawArc(paddingLeft.toFloat()+mLinePaint.strokeWidth,paddingTop.toFloat()+mLinePaint.strokeWidth,paddingLeft+min(contentHeight,contentWidth).toFloat()-mLinePaint.strokeWidth-paddingRight,paddingTop+min(contentHeight,contentWidth).toFloat()-mLinePaint.strokeWidth-paddingBottom,0f,currentRad,false,mLinePaint)mTextPaint.color = mTextColormTextPaint.textSize = mTextSizemTextPaint.style = Paint.Style.FILLmTextPaint.isUnderlineText = truecontentString = (currentRad/360*100).toInt().toString() + "%"val lengthOfString = contentString.length * 25canvas.drawText(contentString,min(contentWidth,contentHeight).toFloat()/2-lengthOfString/2,min(contentWidth,contentHeight).toFloat()/2+lengthOfString/10,mTextPaint)mTextPaint.style = Paint.Style.STROKEif (Debug) {canvas.drawRect(mSizeWithPadding,mTextPaint)canvas.drawRect(paddingLeft.toFloat()+mLinePaint.strokeWidth,paddingTop.toFloat()+mLinePaint.strokeWidth,paddingLeft+min(contentHeight,contentWidth).toFloat()-mLinePaint.strokeWidth-paddingRight,paddingTop+min(contentHeight,contentWidth).toFloat()-mLinePaint.strokeWidth-paddingBottom,mTextPaint)}}
}

最终效果

在这里插入图片描述

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

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

相关文章

JavaScript系列——Promise

文章目录 概要Promise三种状态状态改变Promise链式调用Promise处理并发promise.all()promise.allSettled()Promise.any()promise.race() 小结 概要 Promise中文翻译过来就是承诺、预示、有可能的意思。 在JavaScript里面,Promise 是一个对象…

软件测试|解析selenium.common.exceptions.ElementClickInterceptedException错误及解决方法

导言 在自动化测试中,Selenium是一个广泛使用的工具,用于模拟用户在网页上的操作。然而,有时候在执行点击操作时,可能会遇到ElementClickInterceptedException异常,这可能是由于多种原因导致的。本文将深入探讨这个错…

【算法练习】leetcode算法题合集之数组和哈希表篇

重建数组&#xff08;高频&#xff09; LeetCode283.移动零 LeetCode283.移动零 双指针&#xff0c;记录已经处理好的序列的尾部 class Solution {public void moveZeroes(int[] nums) {int k 0;for (int i 0; i < nums.length; i) {if (nums[i] ! 0) {swap(nums, i, k)…

Kafka的简介及架构

目录 消息队列 产生背景 消息队列介绍 常见的消息队列产品 应用场景 消息队列的消息模型 Kafka的基本介绍 简介 Kafka的架构 Kafka的使用 Kafka的shell命令 Kafka的Python API的操作 完成生产者代码 完成消费者代码 消息队列 产生背景 消息队列:指数据在一个容器…

Linux学习记录——삼십팔 网络层IP协议

文章目录 1、了解IP协议2、IP协议报文1、8位服务类型2、16位总长度&#xff08;字节数&#xff09;3、8位生存时间&#xff08;TTL&#xff09; 3、网段划分1、网段划分和CIDR方案2、子网划分简单方法3、IP地址问题的解决方案 4、公网内网1、内网分配2、运营商管理方法 5、路由…

【Python基础】一文搞懂:Python 中 Excel 文件的写入与读取

文章目录 1 引言2 使用 openpyxl2.1 安装 openpyxl2.2 写入 Excel 文件2.3 读取 Excel 文件 3 使用 pandas3.1 安装 pandas 和 openpyxl3.2 写入 Excel 文件3.3 读取 Excel 文件 4 实例演示4.1 安装所需库4.2 封装为excel_example.py脚本文件 5 注意事项6 总结 1 引言 在现代办…

spring-boot项目启动类错误: 找不到或无法加载主类 com.**Application

问题&#xff1a;Springboot项目启动报错&#xff1a;错误: 找不到或无法加载主类 com.**Application 解决步骤&#xff1a; 1.File–>Project Structure 2.Modules–>选中你的项目–点击“-”移除 3.重新导入&#xff1a;点击“”号&#xff0c;选择Import Module&…

【漏洞复现】优卡特脸爱云一脸通智慧管理平台文件上传漏洞

Nx01 产品简介 脸爱云一脸通智慧管理平台是一套功能强大&#xff0c;运行稳定&#xff0c;操作简单方便&#xff0c;用户界面美观&#xff0c;轻松统计数据的一脸通系统。无需安装&#xff0c;只需在后台配置即可在浏览器登录。 功能包括&#xff1a;系统管理中心、人员信息管…

GPT 商店强势来袭,人人都要有自己的 GPTs

作者&#xff1a;苍何&#xff0c;前大厂高级 Java 工程师&#xff0c;阿里云专家博主&#xff0c;CSDN 2023 年 实力新星&#xff0c;土木转码&#xff0c;现任部门技术 leader&#xff0c;专注于互联网技术分享&#xff0c;职场经验分享。 &#x1f525;热门文章推荐&#xf…

网络基础学习(3):交换机

1.交换机结构 &#xff08;1&#xff09;网线接口和后面的电路部分加在一起称为一个端口&#xff0c;也就是说交换机的一个端口就相当于计算机上的一块网卡。 如果在计算机上安装多个网卡&#xff0c;并让网卡接收所有网络包&#xff0c;再安装具备交换机功能的软件&#xff0…

数据加工:从原始数据到有价值的信息

在当今数字化的时代&#xff0c;数据已经成为了企业和组织最宝贵的资产之一。然而&#xff0c;原始数据往往需要经过加工和处理&#xff0c;才能转化为有价值的信息和知识。数据加工是指将原始数据进行处理和分析&#xff0c;以提取有用的信息和知识的过程。数据加工的重要性不…

idea 设置文件头

idea 设置创建文件时自动添加文档注释信息 /** * Description * Author jimaomao * DATE ${DATE} ${TIME} */

【Kafka-3.x-教程】-【五】Kafka-监控-Eagle

【Kafka-3.x-教程】专栏&#xff1a; 【Kafka-3.x-教程】-【一】Kafka 概述、Kafka 快速入门 【Kafka-3.x-教程】-【二】Kafka-生产者-Producer 【Kafka-3.x-教程】-【三】Kafka-Broker、Kafka-Kraft 【Kafka-3.x-教程】-【四】Kafka-消费者-Consumer 【Kafka-3.x-教程】-【五…

React之自定义路由组件

开篇 react router功能很强大&#xff0c;可以根据路径配置对应容器组件。做到组件的局部刷新&#xff0c;接下来我会基于react实现一个简单的路由组件。 代码 自定义路由组件 import {useEffect, useState} from "react"; import React from react // 路由配置 e…

2000-2021年全国各省环境相关指标数据(890+指标)

2000-2021年全国各省环境相关指标数据&#xff08;890指标&#xff09; 1、指标时间&#xff1a;2000-2021年 2、范围&#xff1a;31省市 3、来源&#xff1a;2001-2022年环境统计年鉴 4、指标&#xff1a;工业废水排放总量、工业废水排放达标量、工业废水处理量、化学需氧…

【野火i.MX6ULL开发板】开发板连接网络(WiFi)与 SSH 登录、上电自动登录、设置静态IP、板子默认参数

0、前言 参考之前自己写的&#xff1a; http://t.csdnimg.cn/g60P8 参考资料&#xff1a; [野火]《Linux基础与应用开发实战指南——基于i.MX6ULL开发板》_20230323 从野火官网下载 参考博客&#xff1a; http://t.csdnimg.cn/8uh4O 参考官方文档&#xff1a; https://doc.…

C++力扣题目257--二叉树的所有路径

给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,null,5] 输出&#xff1a;["1->2->5","1->3"]示例 …

Redis:原理速成+项目实战——Redis实战10(Redis消息队列实现异步秒杀)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;Redis&#xff1a;原理速成项目实战——Redis实战9&#xff08;秒杀优化&#xff09; &#x1f4da;订阅专栏&#xff1a;Redis&…

Eureka切换Nacos时发现两个注册中心的解决方法

报错信息如下&#xff0c;意思是发现了两个注册中心 Field autoServiceRegistration in org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration required a single bean, but 2 were found: - nacosAutoServiceRegistration: defined…

数学经典教材有什么?

有本书叫做《自然哲学的数学原理》&#xff0c;是牛顿写的&#xff0c;读完之后你就会感叹牛顿的厉害之处! 原文完整版PDF&#xff1a;https://pan.quark.cn/s/5d5eac2e56af 那玩意真的是人写出来的么… 现代教材把牛顿力学简化成三定律&#xff0c;当然觉得很简单。只有读了原…