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

2024-02-23 12:12

本文主要是介绍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>

这篇关于Android 圆环带刻度条进度动画效果实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/738594

相关文章

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

PyCharm中配置PyQt的实现步骤

《PyCharm中配置PyQt的实现步骤》PyCharm是JetBrains推出的一款强大的PythonIDE,结合PyQt可以进行pythion高效开发桌面GUI应用程序,本文就来介绍一下PyCha... 目录1. 安装China编程PyQt1.PyQt 核心组件2. 基础 PyQt 应用程序结构3. 使用 Q

Python实现批量提取BLF文件时间戳

《Python实现批量提取BLF文件时间戳》BLF(BinaryLoggingFormat)作为Vector公司推出的CAN总线数据记录格式,被广泛用于存储车辆通信数据,本文将使用Python轻松提取... 目录一、为什么需要批量处理 BLF 文件二、核心代码解析:从文件遍历到数据导出1. 环境准备与依赖库

linux下shell脚本启动jar包实现过程

《linux下shell脚本启动jar包实现过程》确保APP_NAME和LOG_FILE位于目录内,首次启动前需手动创建log文件夹,否则报错,此为个人经验,供参考,欢迎支持脚本之家... 目录linux下shell脚本启动jar包样例1样例2总结linux下shell脚本启动jar包样例1#!/bin