android 彩虹进度条自定义view实现

实现一个彩虹色进度条功能,不说明具体用途大家应该能猜到。想找别人造的轮子,但是没有合适的,所以决定自己实现一个。
相关知识
android 自定义view
LinearGradient 线性渐变
实现步骤
自定义view
自定义一个TmcView类继承View
重写两个构造方法。构造方法一共有4个,这里边重写两个
重写ongSizeChanged方法,用来获取控件宽、高,来计算内部组件尺寸。
重写onDraw方法,里边要描画背景drawBackground,分段数据drawSection,和seekbar图片drawImage。
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;import androidx.annotation.Nullable;import java.util.List;public class TmcView extends View {public TmcView(Context context) {super(context);}public TmcView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawBackground(canvas);drawSection(canvas);drawImage(canvas);}/*** 画背景* @param canvas 画布*/private void drawBackground(Canvas canvas) {}/*** 画分段数据* @param canvas 画布*/private void drawSection(Canvas canvas) {}/*** 画图片* @param canvas 画布*/private void drawImage(Canvas canvas) {}/*** 更新view* @param total 总长度* @param rest 剩余长度* @param sections 分段数据*/public void updateView(int total, int rest, List<SectionData> sections){}
实现几个重构方法
标注:
整体宽度为图片宽度44px
背景条宽度30px,外边距7px,圆角15px
带颜色的条宽度20xp,外边距5px,圆角15px
自定义view的坐标轴是以view的左上角位置为原点,向右为x轴正方向,向下为y轴正方向

在视图中用RectF创建背景,和颜色条,并在onSizeChanged中设置尺寸
public class TmcView extends View {private final RectF backgroundRectF = new RectF();private final RectF colorRectF = new RectF();public TmcView(Context context) {super(context);}public TmcView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);//设置矩形 left top right bottom 左上右下点的值 按照标注计算得出backgroundRectF.set(7, 0, 37, h);colorRectF.set(12, 5, 32, h-5);}...
}
绘制背景条

实现drawBackground方法,画背景需要一根画笔Paint 为了避免重复创建,声明为成员变量
public class TmcView extends View {/*背景画笔*/private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private final RectF backgroundRectF = new RectF();private final RectF colorRectF = new RectF();public TmcView(Context context) {super(context);}public TmcView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);//设置矩形 left top right bottom 左上右下点的值 按照标注计算得出backgroundRectF.set(7, 0, 37, h);colorRectF.set(12, 5, 32, h-5);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawBackground(canvas);drawSection(canvas);drawImage(canvas);}/*** 画背景* @param canvas 画布*/private void drawBackground(Canvas canvas) {backPaint.setStyle(Paint.Style.FILL);backPaint.setAntiAlias(true); //抗锯齿backPaint.setColor(Color.parseColor("#FFFFFF"));//画圆角矩形,15为圆角的角度canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);}
...
}
布局中加入TmcView
<com.bigxuan.tesapp.view.TmcViewandroid:id="@+id/tmc"android:layout_width="44px"android:layout_height="690px"android:layout_marginEnd="1230px"android:layout_marginBottom="100px"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"/>
绘制颜色条
![]()
实现drawSection方法,这里要用到线性渐变LinearGradient
LinearGradient 简单说,指定每一段的颜色和位置百分比,就能实现每一段显示不同颜色。
但它默认是渐变色,要想不变就在每一段的开始和结束位置都设置相同的颜色。
再创建一个画笔 Paint,画颜色条
public class TmcView extends View {private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private final Paint colorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private final RectF backgroundRectF = new RectF();private final RectF colorRectF = new RectF();public TmcView(Context context) {super(context);}public TmcView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);//设置矩形 left top right bottom 左上右下点的值 按照标注计算得出backgroundRectF.set(7, 0, 37, h);colorRectF.set(12, 5, 32, h-5);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawBackground(canvas);drawSection(canvas);drawImage(canvas);}/*** 画背景* @param canvas 画布*/private void drawBackground(Canvas canvas) {backPaint.setStyle(Paint.Style.FILL);backPaint.setAntiAlias(true); //抗锯齿backPaint.setColor(Color.parseColor("#FFFFFF"));//画圆角矩形,15为圆角的角度canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);}/*** 画分段数据* @param canvas 画布*/private void drawSection(Canvas canvas) {int[] colorArray = {Color.parseColor("#FF0000"), Color.parseColor("#FF0000"),Color.parseColor("#00FF00"), Color.parseColor("#00FF00"),Color.parseColor("#0000FF"), Color.parseColor("#0000FF")};float[] positionArray = {0f, 0.2f,0.2f, 0.6f,0.6f, 1f};colorPaint.setStyle(Paint.Style.FILL);colorPaint.setAntiAlias(true);//指定渐变色方向x轴方向不变,沿y方向渐变,渐变范围为 颜色条高度colorPaint.setShader(new LinearGradient(0, 0, 0, colorRectF.bottom, colorArray, positionArray, Shader.TileMode.REPEAT));canvas.drawRoundRect(colorRectF,15, 15, colorPaint);}...
}
绘制进度图片

app加入图片资源,根据资源id获取bitmap对象,绘制。
需要注意的是,坐标轴的顶点在左上角,绘制图片时也是以图片左上顶点位置做定位,图片位置是view的高度减掉图片的高度,才能显示在正确位置。
public class TmcView extends View {private Context context;private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private final Paint colorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private final RectF backgroundRectF = new RectF();private final RectF colorRectF = new RectF();/*图片资源id*/private int imgResId;/*图片位置*/private int imgPos;public TmcView(Context context) {super(context);}public TmcView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);this.context = context;this.imgResId = R.drawable.icon_seekbar_day;}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);//设置矩形 left top right bottom 左上右下点的值 按照标注计算得出backgroundRectF.set(7, 0, 37, h);colorRectF.set(12, 5, 32, h-5);imgPos = h - 44; // 设置一个初始位置}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawBackground(canvas);drawSection(canvas);drawImage(canvas);}/*** 画背景* @param canvas 画布*/private void drawBackground(Canvas canvas) {backPaint.setStyle(Paint.Style.FILL);backPaint.setAntiAlias(true); //抗锯齿backPaint.setColor(Color.parseColor("#FFFFFF"));//画圆角矩形,15为圆角的角度canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);}/*** 画分段数据* @param canvas 画布*/private void drawSection(Canvas canvas) {int[] colorArray = {Color.parseColor("#FF0000"), Color.parseColor("#FF0000"),Color.parseColor("#00FF00"), Color.parseColor("#00FF00"),Color.parseColor("#0000FF"), Color.parseColor("#0000FF")};float[] positionArray = {0f, 0.2f,0.2f, 0.6f,0.6f, 1f};colorPaint.setStyle(Paint.Style.FILL);colorPaint.setAntiAlias(true);//指定渐变色方向x轴方向不变,沿y方向渐变,渐变范围为 颜色条高度colorPaint.setShader(new LinearGradient(0, 0, 0, colorRectF.bottom, colorArray, positionArray, Shader.TileMode.REPEAT));canvas.drawRoundRect(colorRectF,15, 15, colorPaint);}/*** 画图片* @param canvas 画布*/private void drawImage(Canvas canvas) {Bitmap bitmap = initBitmap(imgResId);canvas.save();canvas.translate(0, 0);canvas.drawBitmap(bitmap, 0, imgPos, null);canvas.restore();}/*** 通过资源id 创建bitmap* @param resId 资源id* @return*/private Bitmap initBitmap(int resId) {Bitmap originalBmp = BitmapFactory.decodeResource(context.getResources(), resId);return Bitmap.createScaledBitmap(originalBmp, 44, 44, true);}
...
}
公共方法
暴露方法用来更新进度updateView
定义一个图片有效的运动距离为view高度减掉图片高度,函数参数为总距离和剩余距离,计算百分比后乘以有效运动距离得出图片描画位置。最后调用invalidate方法刷新view
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;import androidx.annotation.Nullable;import com.navinfo.tesapp.R;import java.util.List;public class TmcView extends View {private Context context;private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private final Paint colorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private final RectF backgroundRectF = new RectF();private final RectF colorRectF = new RectF();/*图片资源id*/private int imgResId;/*图片位置*/private int imgPos;/*图片有效的运动距离*/private int imgValidHeight;public TmcView(Context context) {super(context);}public TmcView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);this.context = context;this.imgResId = R.drawable.icon_seekbar_day;}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);//设置矩形 left top right bottom 左上右下点的值 按照标注计算得出backgroundRectF.set(7, 0, 37, h);colorRectF.set(12, 5, 32, h-5);imgValidHeight = h - 44;imgPos = h - 44; // 设置一个初始位置}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawBackground(canvas);drawSection(canvas);drawImage(canvas);}/*** 画背景* @param canvas 画布*/private void drawBackground(Canvas canvas) {backPaint.setStyle(Paint.Style.FILL);backPaint.setAntiAlias(true); //抗锯齿backPaint.setColor(Color.parseColor("#FFFFFF"));//画圆角矩形,15为圆角的角度canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);}/*** 画分段数据* @param canvas 画布*/private void drawSection(Canvas canvas) {int[] colorArray = {Color.parseColor("#FF0000"), Color.parseColor("#FF0000"),Color.parseColor("#00FF00"), Color.parseColor("#00FF00"),Color.parseColor("#0000FF"), Color.parseColor("#0000FF")};float[] positionArray = {0f, 0.2f,0.2f, 0.6f,0.6f, 1f};colorPaint.setStyle(Paint.Style.FILL);colorPaint.setAntiAlias(true);//指定渐变色方向x轴方向不变,沿y方向渐变,渐变范围为 颜色条高度colorPaint.setShader(new LinearGradient(0, 0, 0, colorRectF.bottom, colorArray, positionArray, Shader.TileMode.REPEAT));canvas.drawRoundRect(colorRectF,15, 15, colorPaint);}/*** 画图片* @param canvas 画布*/private void drawImage(Canvas canvas) {Bitmap bitmap = initBitmap(imgResId);canvas.save();canvas.translate(0, 0);canvas.drawBitmap(bitmap, 0, imgPos, null);canvas.restore();}/**** @param resId* @return*/private Bitmap initBitmap(int resId) {Bitmap originalBmp = BitmapFactory.decodeResource(context.getResources(), resId);return Bitmap.createScaledBitmap(originalBmp, 44, 44, true);}/*** 更新view* @param total 总长度* @param rest 剩余长度* @param sections 分段数据*/public void updateView(int total, int rest, List<SectionData> sections){float percent = (1f * rest) / total;//防止溢出if(percent < 0){return;}imgPos = (int)(percent * imgValidHeight);invalidate();}
}
updateView方法还有第三个参数,是用来传出颜色条不同段的颜色和百分比数据的。根据此数据来更新颜色条。这里需要根据业务不同自己实现,我这里就不写了。
总结
大家可能看出来了,这个视图是用来展示导航中不同路段交通情况和当前车辆进度用的。自定义view中可以在构造方法中获取一些自定义属性,像背景条和颜色条的边距、圆角这些都可以定义到xml中,因为只适配一种屏幕尺寸所以也没有做多尺寸适配。以前也没有做过,这次记录下来。
相关文章:
android 彩虹进度条自定义view实现
实现一个彩虹色进度条功能,不说明具体用途大家应该能猜到。想找别人造的轮子,但是没有合适的,所以决定自己实现一个。 相关知识 android 自定义view LinearGradient 线性渐变 实现步骤 自定义view 自定义一个TmcView类继承View 重写两…...
免费一年SSL证书申请——建议收藏
免费一年SSL证书申请——建议收藏 获取免费一年期SSL证书其实挺简单的 准备你的网站: 确保你的网站已经有了域名,而且这个域名已经指向你的服务器。还要检查你的服务器支持HTTPS,也就是443端口要打开,这是HTTPS默认用的。 验证域…...
【docker1】指令,docker-compose,Dockerfile
文章目录 1.pull/image,run/ps(进程),exec/commit2.save/load:docker save 镜像id,不是容器id3.docker-compose:多容器:宿主机(eth0网卡)安装docker会生成一…...
Flutter中的异步和多进程
Flutter 是一个用于创建高性能、高保真度移动应用的框架,它使用 Dart 编程语言。 在 Flutter 中,异步和多进程是两种不同的概念,用于解决不同的问题。 异步 (Asynchronous) 异步编程是一种编程范式,允许代码在等待操作完成(如网络请求、文件 I/O)时继续执行其他任务,而不…...
学习C++第二天
1.缺省参数 缺省参数的概念: 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。 void show(int a 10) {cout << a << endl; }int main() {sho…...
解析Java中1000个常用类:AbstractSet类,你学会了吗?
推荐一个我自己写的小报童专栏导航网站: http://xbt100.top 收录了生财有术项目精选、AI海外赚钱、纯银的产品分析等专栏,陆续会收录更多的专栏,欢迎体验~复制URL可直达。 以下是正文。 在 Java 集合框架中,AbstractSet 是一个重要的抽象类,为实现自定义的集合(Set)提…...
Nginx基础概念和常用操作
文章目录 1. 安装、启动、连接2. 快速尝试部署网站3. 配置文件1. nginx.conf全局配置事件模块HTTP 模块性能优化建议 2. default.confserver 块基本设置日志设置根路径设置 4. 反向代理1. 模拟3个Web2. 链接 5. 负载均衡1. 加权轮询,Weighted Round Robin2. 最少连接…...
圈复杂度是什么?go语言调整圈复杂度举例
圈复杂度(也称为循环复杂度或McCabe复杂度)是衡量程序复杂性的一个指标,它通常与代码中的控制流结构(如条件语句、循环和函数调用)的数量相关。在Go语言中,你可以通过重构代码来降低圈复杂度,从…...
设计模式4-模版方法
设计模式 重构获得模式重构的关键技法1. 静态转动态2. 早绑定转晚绑定3. 继承转组合4. 编译时依赖转运行时依赖5. 紧耦合转松耦合 组件协助动机模式定义结构 要点总结。 例子示例解释: 重构获得模式 设计模式的目的是应对变化,提高复用 设计模式的要点…...
yii2 ActiveForm使用技巧
持续更新: 1、搜索输入框:form-inline <?php $form ActiveForm::begin([action > [index],method > get,options > [class > form-inline] (增加此行代码) ]); ?>...
【面试】基本数据类型的包装类缓存
目录 1. 说明2. Integer类分析2.1 代码块2.2 字节码2.3 分析2.4 valueOf方法 1. 说明 1.在java中,基本数据类型的包装类(Integer、Byte、Character、Short、Long、Boolean)的某些值会被缓存。2.以提高性能并减少内存使用。3.这种缓存机制是自…...
6月20日(周四)A股行情总结:A股险守3000点,恒生科技指数跌1.6%
A股三大股指走弱,科创板逆势上扬,半导体板块走强,多股20CM涨停。中芯国际港股涨超1%。恒生科技指数跌超1%。离岸人民币对美元汇率小幅走低,20日盘中最低跌至7.2874,创下2023年11月中旬以来的新低,随后收复部…...
Parallels Desktop 19 for mac破解版安装激活使用指南
Parallels Desktop 19 for Mac 乃是一款适配于 Mac 的虚拟化软件。它能让您在 Mac 计算机上同时运行多个操作系统。您可借此创建虚拟机,并于其中装设不同的操作系统,如 Windows、Linux 或 macOS。使用 Parallels Desktop 19 mac 版时,您可在 …...
JExcel API使用笔记
JExcel API使用笔记 JExcel是一个开源的支持excel的java类库,广泛利用其api来生成excel报表 API基本使用 1.创建excel文件 workbook Workbook.createWorkbook(file);//传入file文件2.创建sheet页 WritableSheet sheet workbook.createSheet("记录表&quo…...
springCloudAlibaba之分布式网关组件---gateway
gateway-网关 网关spring cloud gatewaygateway初体验gateway整合nacos简写方式 内置路由断言工厂内置断言工厂 自定义路由断言工厂自定义路由工厂 内置/自定义过滤器典型内置过滤器自定义过滤器 全局过滤器自定义全局过滤器 请求日志记录&跨域处理Gateway跨域配置…...
Springboot项目jar加密
部署的程序进行加密,防止第三方非法拷贝走项目进行二次开发或部署。我们知道java代码编译后生成的以.class结尾的字节码文件或者.jar/.war结尾的可执行文件都是可以反编译生成.java文件的,虽然反编译后生成的.java文件和原本的.java文件有些微差别&#…...
【React】高阶组件
概述 高阶组件并非一个组件,而是增强组件功能的一个函数。 高阶组件的作用是对多个组件公共逻辑进行横向抽离。 高阶组件 – React (reactjs.org) 示例 ChildCom1.jsx import React from react;function ChildCom1(props) {return (<div>这是子组件1<d…...
全面理解-Flutter(万字长文,深度解析)
1、Web 性能差,跟原生 App 存在肉眼可见的差距; 2、React Native 跟 Web 相比,支持的能力非常有限,特定长场景问题,需要三端团队一个一个处理; 3、Web 浏览器的安卓碎片化严重(感谢 X5&#x…...
RabbitMQ实战宝典:从新手到专家的全面探索
前言 在当今分布式系统架构中,消息队列已成为不可或缺的一部分,而RabbitMQ作为其中的佼佼者,凭借其强大的功能和灵活性,广泛应用于各种规模的应用场景中。本文将带你从基础概念出发,深入探讨RabbitMQ的核心特性&#…...
6月21日(周五)AH股总结:沪指失守3000点,恒生科技指数跌近2%,多只沪深300ETF午后量能显著放大
内容提要 沪指全天围绕3000点关口来回拉锯,收盘跌破3000点。白酒及光刻机概念集体走低,中芯国际港股跌超2%。CRO医药概念及水利股逆势走强。 A股低开低走 沪指全天围绕3000点关口来回拉锯,收盘跌破3000点,跌0.24%。深成指跌0.04…...
OpenClaw技能开发入门:为千问3.5-27B编写自定义模块
OpenClaw技能开发入门:为千问3.5-27B编写自定义模块 1. 为什么需要自定义技能? 去年冬天,我发现自己每天早晨都要手动查询天气并发送给家人。重复的操作让我开始思考:能否让OpenClaw帮我自动完成这个任务?这就是我踏…...
Pixel Aurora Engine基础教程:Streamlit状态管理与多会话隔离机制
Pixel Aurora Engine基础教程:Streamlit状态管理与多会话隔离机制 1. 认识Pixel Aurora Engine Pixel Aurora是一款基于AI扩散模型的高端绘图工作站,采用独特的复古像素游戏风格界面。这款"虚拟游戏机"能将文字描述转化为极具视觉冲击力的像…...
Pixel Couplet Gen实操手册:微信小程序分包加载优化像素春联H5首屏速度
Pixel Couplet Gen实操手册:微信小程序分包加载优化像素春联H5首屏速度 1. 项目背景与核心价值 Pixel Couplet Gen是一款融合传统春节文化与现代像素艺术风格的创新应用。通过ModelScope大模型的文本生成能力,结合精心设计的8-bit视觉元素,…...
SEO网站广告如何与本地化营销相结合
SEO网站广告与本地化营销的结合:如何提升本地企业的市场竞争力 在当今数字化经济的浪潮中,SEO网站广告和本地化营销已经成为企业营销的两大重要手段。如何将这两者有机地结合,以实现最大的营销效益,是许多企业面临的重要课题。本…...
Intv_AI_MK11 解决 403 Forbidden 错误:模型服务访问权限配置详解
Intv_AI_MK11 解决 403 Forbidden 错误:模型服务访问权限配置详解 1. 问题背景与解决思路 当你兴致勃勃地准备调用 Intv_AI_MK11 模型服务时,突然收到一个冷冰冰的 "403 Forbidden" 错误,这种体验就像拿着门票却被拦在演唱会门外…...
QT 生成动态链接库
QT 生成动态链接库 前言 一、创建新的动态库项目(Qt Creator) 1 新建项目 二 、 自动生成的文件结构 1 项目会包含一个导出宏定义头文件,例如 Test001_global.h: 2 在需要导出的类或函数前加上 TEST001_EXPORT(我自己测试不加也行): 3 crtl+B 或者点击左下角锤子 进行编译…...
重磅发布!集装箱式SST直流移动智算中心
NEWS3月28日,台达、汉腾科技与龙芯中科联合宣布重磅发布集装箱式 SST(固态变压器)直流移动智算中心,发布活动于台达吴江制造基地举行。这款全新方案以台达 SST 固态变压器为核心能源支撑,深度集成CPU、AI 加速卡与服务…...
【FMCW雷达】频率调制连续波FMCW雷达系统(从波形生成到利用小胞平均常误报率CA-CFAR进行目标检测)【含Matlab源码 15242期】含报告
💥💥💥💥💥💥💥💥💞💞💞💞💞💞💞💞💞Matlab武动乾坤博客之家💞…...
TP4056充电板实战避坑指南:从LED状态误判到TEMP脚悬空,新手最容易踩的5个坑
TP4056充电板实战避坑指南:从LED状态误判到TEMP脚悬空,新手最容易踩的5个坑 第一次使用TP4056充电板时,我盯着闪烁的LED灯陷入了困惑——为什么充满电后红灯还亮着?为什么电池发热异常?这些问题让我意识到,…...
Polars 2.0清洗性能天花板在哪?实测对比Dask/Modin/Vaex:单机1TB数据清洗仅需11.3秒(附完整安装脚本)
第一章:Polars 2.0 大规模数据清洗技巧Polars 2.0 引入了更严格的惰性执行模型、增强的字符串与时间处理能力,以及原生支持多线程 I/O 的 LazyFrame API,显著提升了 TB 级数据清洗的吞吐与可控性。相比 Pandas,其列式内存布局与零…...
