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

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import