Android 圆环带刻度条进度动画效果实现

效果图
圆环带刻度进度

需求是根据传感器做一个重力球效果,先实现了动画后续加上跟传感器联动.
又是摆烂的一天, 尚能呼吸,未来可期啊

View源码

package com.android.circlescalebar.view;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
import com.android.circlescalebar.R;
import com.android.circlescalebar.utils.ChartUtils;
import com.android.circlescalebar.utils.DensityUtils;public class CircleGearView extends View {private Context mContext;private Paint mPaint; // 画笔对象的引用private PointF mProgressPoint;private float mRoundWidth = DensityUtils.dp2px(4); // 圆环的宽度private int centerX, centerY;private int radius, roundRadius;private int paddingOuterThumb;//外边距private int minValidateTouchArcRadius; // 最小有效点击半径private int maxValidateTouchArcRadius; // 最大有效点击半径private int mMainColor; //主题颜色private int mInnerRoundColor; //内圆 宽度 、颜色private float mInnerRoundWidth;private int mTxtProgress = 1; // 显示进度private int max = 200; // 最大进度 -- 总共200个刻度 所以这样定义private float progress = 1;private double mOuterRoundProgress = 0f;//外圈进度private boolean mOuterSences = true; //true 正向----false方向public CircleGearView(Context context) {this(context, null);}public CircleGearView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public CircleGearView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mContext = context;initView(attrs);}private void initView(AttributeSet attrs){setLayerType(View.LAYER_TYPE_SOFTWARE, null);  // 关闭硬件加速this.setWillNotDraw(false);                    // 调用此方法后,才会执行 onDraw(Canvas) 方法mPaint = new Paint();//获取自定义属性和默认值TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.CGViewStyleable);mRoundWidth = typedArray.getDimension(R.styleable.CGViewStyleable_round_width, DensityUtils.dp2px(7));mMainColor = typedArray.getColor(R.styleable.CGViewStyleable_round_color, getResources().getColor(R.color.green));mInnerRoundWidth = typedArray.getDimension(R.styleable.CGViewStyleable_inner_round_width, DensityUtils.dp2px(2));mInnerRoundColor = typedArray.getColor(R.styleable.CGViewStyleable_inner_round_color, getResources().getColor(R.color.white33));paddingOuterThumb = DensityUtils.dp2px(20);}@Overrideprotected void onSizeChanged(int width, int height, int oldw, int oldh) {centerX = width / 2;centerY = height / 2;int minCenter = Math.min(centerX, centerY);radius = (int) (minCenter - mRoundWidth / 2 - paddingOuterThumb); //圆环的半径roundRadius = radius - (int)(3 * mRoundWidth);minValidateTouchArcRadius = (int) (radius - paddingOuterThumb * 1.5f);maxValidateTouchArcRadius = (int) (radius + paddingOuterThumb * 1.5f);super.onSizeChanged(width, height, oldw, oldh);}@Overridepublic void onDraw(Canvas canvas) {//  setLayerType(LAYER_TYPE_SOFTWARE, null);//对单独的View在运行时阶段禁用硬件加速initOnDraw(canvas);}/** start circle -*/private void initOnDraw(Canvas canvas) {/** 画刻度-200份- 还分正反切换---start */mPaint.setStrokeWidth(DensityUtils.dp2px(1));for (int i = 0; i < 200; i++){//radius:模糊半径,radius越大越模糊,越小越清晰,但是如果radius设置为0,则阴影消失不见//dx:阴影的横向偏移距离,正值向右偏移,负值向左偏移//dy:阴影的纵向偏移距离,正值向下偏移,负值向上偏移//color: 绘制阴影的画笔颜色,即阴影的颜色(对图片阴影无效)if (i < mOuterRoundProgress) {if (mOuterSences) {
//                    mPaint.setShadowLayer(30, 0, 0, mMainColor);mPaint.setColor(getResources().getColor(R.color.green));} elsemPaint.setColor(getResources().getColor(R.color.white33));} else {if (mOuterSences)mPaint.setColor(getResources().getColor(R.color.white33));else {
//                    mPaint.setShadowLayer(30, 0, 0, mMainColor);mPaint.setColor(getResources().getColor(R.color.green));}}float mProgress = (i)* 1.0f/ 200 * max;PointF mProgressPoint = ChartUtils.calcArcEndPointXY(centerX, centerY, radius, 360 * mProgress / max, 90);//圆上到圆心float scale1 = radius * 1.0F / mRoundWidth;float scale2 = radius * 1.0F / (radius - mRoundWidth);//计算内圆上的点float disX = (scale1*mProgressPoint.x + scale2*centerX)/(scale1+ scale2);float disY =  (scale1*mProgressPoint.y + scale2*centerY)/(scale1+ scale2);//计算外圆上的点float disX2 = mProgressPoint.x*2 - disX;float disY2 =  mProgressPoint.y*2 - disY;
//            if (mProgress%6 == 0){
//                //直线3/4高度
//                canvas.drawLine(disX2 ,disY2,disX,disY, mPaint);
//            }else{//直线1/2高度float disX3 = (disX*1 + disX2)/2;float disY3 =  (disY*1 + disY2)/2;canvas.drawLine(disX3 ,disY3,disX,disY, mPaint);
//            }}/** 画刻度-200份- 还分正反切换---end */// 移动圆点mProgressPoint = ChartUtils.calcArcEndPointXY(centerX, centerY, radius - 55, 360 *progress / max, (float)90);
//        //直接用画笔画mPaint.setColor(getResources().getColor(R.color.green));  //设置进度的颜色// 设置渐变
//        Shader shader = new RadialGradient(
//                0, 0, 50, // 圆的中心坐标和半径
//                mMainColor, mInnerRoundColor, // 渐变的起止颜色
//                Shader.TileMode.CLAMP // 渐变模式
//        );
//        mPaint.setShader(shader);canvas.drawCircle(mProgressPoint.x, mProgressPoint.y,30 ,mPaint);canvas.restore();canvas.save();}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();}/*** 设置进度,此为线程安全控件,由于考虑多线的问题,需要同步* 刷新界面调用postInvalidate()能在非UI线程刷新** @param progress*/public synchronized void setProgress(float progress) {if (progress < 0) {mTxtProgress = 1;progress = 0;}mTxtProgress =  Math.round(progress);float ss = progress * 200 / 100;progress = (int) ss;if (progress < 0) {throw new IllegalArgumentException("progress not less than 0");}if (progress > max) {progress = max;mOuterRoundProgress = progress + 1;}if (progress <= max) {this.progress = progress;mOuterRoundProgress = progress + 1;postInvalidate();}}
}

工具类

package com.android.circlescalebar.utils;import android.graphics.PointF;public class ChartUtils {/*** 依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标** @param cirX     圆centerX* @param cirY     圆centerY* @param radius   圆半径* @param cirAngle 当前弧角度* @return 扇形终射线与圆弧交叉点的xy坐标*/public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, floatcirAngle) {float posX = 0.0f;float posY = 0.0f;//将角度转换为弧度float arcAngle = (float) (Math.PI * cirAngle / 180.0);if (cirAngle < 90) {posX = cirX + (float) (Math.cos(arcAngle)) * radius;posY = cirY + (float) (Math.sin(arcAngle)) * radius;} else if (cirAngle == 90) {posX = cirX;posY = cirY + radius;} else if (cirAngle > 90 && cirAngle < 180) {arcAngle = (float) (Math.PI * (180 - cirAngle) / 180.0);posX = cirX - (float) (Math.cos(arcAngle)) * radius;posY = cirY + (float) (Math.sin(arcAngle)) * radius;} else if (cirAngle == 180) {posX = cirX - radius;posY = cirY;} else if (cirAngle > 180 && cirAngle < 270) {arcAngle = (float) (Math.PI * (cirAngle - 180) / 180.0);posX = cirX - (float) (Math.cos(arcAngle)) * radius;posY = cirY - (float) (Math.sin(arcAngle)) * radius;} else if (cirAngle == 270) {posX = cirX;posY = cirY - radius;} else {arcAngle = (float) (Math.PI * (360 - cirAngle) / 180.0);posX = cirX + (float) (Math.cos(arcAngle)) * radius;posY = cirY - (float) (Math.sin(arcAngle)) * radius;}return new PointF(posX, posY);}/*** 依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标** @param cirX       圆centerX* @param cirY       圆centerY* @param radius     圆半径* @param cirAngle   当前弧角度* @param orginAngle 起点弧角度* @return 扇形终射线与圆弧交叉点的xy坐标*/public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, floatcirAngle, float orginAngle) {cirAngle = (orginAngle + cirAngle) % 360;return calcArcEndPointXY(cirX, cirY, radius, cirAngle);}
}
package com.android.circlescalebar.utils;import android.content.res.Resources;public class DensityUtils {public float density;public DensityUtils() {density = Resources.getSystem().getDisplayMetrics().density;}/*** 根据手机的分辨率从 dp 的单位 转成为 px(像素)* @param dpValue 虚拟像素* @return 像素*/public static int dp2px(float dpValue) {return (int) (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);}/*** 根据手机的分辨率从 px(像素) 的单位 转成为 dp* @param pxValue 像素* @return 虚拟像素*/public static float px2dp(int pxValue) {return (pxValue / Resources.getSystem().getDisplayMetrics().density);}/*** 根据手机的分辨率从 dp 的单位 转成为 px(像素)* @param dpValue 虚拟像素* @return 像素*/public int dip2px(float dpValue) {return (int) (0.5f + dpValue * density);}/*** 根据手机的分辨率从 px(像素) 的单位 转成为 dp* @param pxValue 像素* @return 虚拟像素*/public float px2dip(int pxValue) {return (pxValue / density);}
}

调用实现

    private int count = 0;  private Handler handler = new Handler();  private Runnable updateTextRunnable;  private CircleGearView circleGearView;@Override  protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  circleGearView = findViewById(R.id.circleGearView); updateTextRunnable = new Runnable() {  @Override  public void run() {  circleGearView.setProgress(count);count++;  if (count > 100) {  // 停止循环  handler.removeCallbacks(this);  } else {  // 继续循环  handler.postDelayed(this, 1000); // 每秒更新一次  }  }  };  // 开始循环  handler.post(updateTextRunnable);  }  @Override  protected void onDestroy() {  super.onDestroy();  // 确保在Activity销毁时移除所有回调和消息,防止内存泄漏  handler.removeCallbacks(updateTextRunnable);  }  

布局

    <com.android.circlescalebar.view.CircleGearViewandroid:id="@+id/circleGearView"android:layout_width="match_parent"android:layout_height="match_parent"app:inner_round_color="@color/white33"app:inner_round_width="2dp"app:round_color="@color/green"app:round_width="7dp" />  

attrs

    <declare-styleable name="CGViewStyleable"><!-- 圆的宽度 --><attr name="round_width" format="dimension"/><attr name="round_color" format="color"/><attr name="inner_round_width" format="dimension"/><attr name="inner_round_color" format="color"/></declare-styleable>

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

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

相关文章

C++ //练习 7.58 下面的静态数据成员的声明和定义有错误吗?请解释原因。

C Primer&#xff08;第5版&#xff09; 练习 7.58 练习 7.58 下面的静态数据成员的声明和定义有错误吗&#xff1f;请解释原因。 //example.h class Example{public:static double rate 6.5;static const int vecSize 20;static vector<double> vec(vecSize); };//e…

【治愈系】心灵鸡汤美文:温暖你的每一寸心田

1.人生就像一杯茶&#xff0c;不会苦一辈子&#xff0c;但总会苦一阵子。只有经历过苦涩&#xff0c;才能品味到甜美的滋味。 2.每一次失败都是一次宝贵的经验&#xff0c;它教会我们如何更好地面对困难和挑战。不要害怕失败&#xff0c;因为失败是成功的前奏。 3.人生最重要的…

【Vue】本地使用 axios 调用第三方接口并处理跨域

前端处理跨域 一. 开发准备 开发工具&#xff1a;VScode框架&#xff1a;Vue2项目结构&#xff1a;vue脚手架生成的标准项目&#xff08;以下仅显示主要部分&#xff09; 本地已搭建好的端口&#xff1a;8080要请求的第三方接口&#xff1a;http://1.11.1.111:端口号/xxx-api…

删除文件中的注释(C语言)

【题目描述】删除文件中的注释&#xff1a;将C语言源程序(hello.c)文件中的所有注释去掉后存入另一个文件(new_hello.c)。试编写相应程序。 【代码】 #include <stdio.h> #include <stdlib.h> int main(void) {FILE *fp1, *fp2;if ((fp1fopen("hello.c"…

【Git工具实战】实用真实 Git 开发工作流程

前言 最近工作中发现&#xff0c;很多开发人员连最基本的Git怎么使用都不知道&#xff0c;比如什么时候切分支&#xff0c;什么时候合并代码&#xff0c;代码遇到冲突怎么办&#xff0c;经常出现掉代码&#xff0c;代码合并后丢失的情况。以下为个人总结的常规Git开发工作流程…

Java架构师之路五、微服务:微服务架构、服务注册与发现、服务治理、服务监控、容器化等。

目录 微服务架构&#xff1a; 服务注册与发现&#xff1a; 服务治理&#xff1a; 服务监控&#xff1a; 容器化&#xff1a; 上篇&#xff1a;Java架构师之路四、分布式系统&#xff1a;分布式架构、分布式数据存储、分布式事务、分布式锁、分布式缓存、分布式消息中间件、…

C语言系列15——C语言的安全性与防御性编程

目录 写在开头1 缓冲区溢出&#xff1a;如何防范与处理1.1 缓冲区溢出的原因1.2 预防与处理策略 2. 安全的字符串处理函数与使用技巧2.1 strncpy函数2.2 snprintf函数2.3 strlcpy函数2.4 使用技巧 3 防御性编程的基本原则与实际方法3.1 基本原则3.2 实际方法 写在最后 写在开头…

思腾合力携京东打造服务器采购解决方案,助企业高校提升算力

随着云计算、大数据、人工智能的快速发展&#xff0c;服务器需求不断扩大&#xff0c;市场规模持续保持增长。IDC数据显示&#xff0c;预计2023年我国服务器市场规模将增至308亿美元。基于对服务器市场的趋势洞察&#xff0c;思腾合力携手京东品牌持续深化合作&#xff0c;在保…

深入浅出JVM(六)之前端编译过程与语法糖原理

本篇文章将围绕Java中的编译器&#xff0c;深入浅出的解析前端编译的流程、泛型、条件编译、增强for循环、可变长参数、lambda表达式等语法糖原理 编译器与执行引擎 编译器 Java中的编译器不止一种&#xff0c;Java编译器可以分为&#xff1a;前端编译器、即时编译器和提前编…

(提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战

文章目录 &#xff08;提供数据集下载&#xff09;基于大语言模型LangChain与ChatGLM3-6B本地知识库调优&#xff1a;数据集优化、参数调整、提示词Prompt优化本地知识库目标操作步骤问答测试的预设问题原始数据情况数据集优化&#xff1a;预处理&#xff0c;先后准备了三份数据…

mac下C、C++项目出现‘stdio.h’ file not found的解决方法

【转载】https://www.cnblogs.com/yongfengnice/p/14260997.html 有时候更新mac系统或者项目配置之后&#xff0c;打开之前的项目&#xff0c;发现出现莫名其妙的‘stdio.h’ file not found等头文件找不到。 解决这个问题之前&#xff0c;我们要弄清楚开发工具是引用了系统哪…

C++:STL简介

1. 什么是STL STL(standard template libaray- 标准模板库 ) &#xff1a; 是 C 标准库的重要组成部分 &#xff0c;不仅是一个可复用的组件库&#xff0c;而且 是一个包罗数据结构与算法的软件框架 。 2. STL的版本 3. STL的六大组件 4.STL的缺陷 1. STL库的更新太慢了。这…

用于将Grafana默认数据库sqlite3迁移到MySQL数据库

以下是一个方案&#xff0c;用于将Grafana数据迁移到MySQL数据库。 背景: grafana 默认采用的是sqlite3&#xff0c;当我们要以集群形式部署的时使用mysql较为方便&#xff0c;试了很多sqlite转mysql的方法要么收费,最后放弃。选择自己动手风衣足食。 目标: 迁移sqlite3切换…

速评谷歌开源大模型Gemma 7B

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

day16_ListSet课后练习题 - 参考答案

文章目录 day16_课后练习题第1题第2题第3题第4题第5题第6题第7题第8题 day16_课后练习题 第1题 案例&#xff1a; ​ 1、用一个String[]数组存点数 ​ 2、用一个String[]数组存花色 ​ 3、用一个String[]数组存大王、小王 ​ 4、用上面的数组&#xff0c;生成一副扑克牌 …

C++ 文件操作-文本文件-读取和打开文件方法详解

读文件步骤 #include <iostream> using namespace std; #include <fstream> #include <string> //文本文件 读文件void test(){// 1 包含头文件// 2 创建流对象ifstream ifs;// 3 打开文件 并且判断是否打开成功ifs.open("table.txt",ios::in); //…

VS 2015 发布 WebService

本文介绍了使用VS2015发布WebService的步骤 右键项目点击发布 选择文件系统和目标位置 配置选择Debug-Any CPU&#xff08;选其他也可以&#xff09; 4. 点击发布&#xff0c;在对应文件夹中可以看到发布出来的内容。 记录遇到的问题&#xff0c; 发布前要选择删除所有现有文…

【PostgreSQL】PostgreSQL详细介绍

PostgreSQL详细介绍 一、什么是PostgreSQL&#xff1f;二、为什么要使用PostgreSQL&#xff1f;三、PostgreSQL功能列表3.1 数据类型3.2 数据完整性3.3 并发&#xff0c;性能3.4 可靠性、灾难恢复3.5 安全3.6 可扩展性3.7 国际化&#xff0c;文本搜索 四、参考资料 关于Postgre…

使用MongoDB数据库和Mongoose库在Node.js中进行数据存储

在Node.js中使用MongoDB数据库和Mongoose库进行数据存储是前端开发中常用的技术之一。MongoDB是一种非关系型数据库&#xff0c;具有高性能、易扩展等优点&#xff1b;而Mongoose是在Node.js中对MongoDB进行操作的框架&#xff0c;简化了数据库操作&#xff0c;并提供了丰富的功…

音视频技术-双声道立体声与卡侬平衡线的“纠葛”

目录 一、新问题 二、问题排查 三、故障总结 四、原理分析 五、解决方案 1、救急 2、转接线1 3、转接线2