当前位置: 首页 > news >正文

Android自定义View-圆形渐变多点的加载框

本文主要记录创建一个 Android 自定义加载弹窗,实现指定个数且从小到大的实心圆周期性旋转的效果。
附上效果如下:
请添加图片描述

一: 自定义圆形loadingView

1.1 核心属性定义
 //默认参数private int circleCount = 8; // 圆圈数量private int minCircleRadius = 5; // 最小圆圈半径private int maxCircleRadius = 15; // 最大圆圈半径private int circleColor = Color.parseColor("#3F51B5"); //默认颜色

这些属性控制了加载动画的外观:圆的数量、大小范围、颜色以及动画实例。

1.2 构造方法
 public CircleLoadingView(Context context) {super(context);init(null);}public CircleLoadingView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init(attrs);}public CircleLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(attrs);}
  • 实现了 3 个构造方法,覆盖了代码创建和 XML 布局引用两种场景
  • 所有构造方法最终都调用 init() 方法完成初始化,符合 Android 自定义 View 的最佳实践
1.3 初始化方法 init()
  private void init(AttributeSet attrs) {if (attrs != null){TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.CircleLoadingView);circleCount = ta.getInt(R.styleable.CircleLoadingView_circleCount, circleCount);minCircleRadius = ta.getInt(R.styleable.CircleLoadingView_minCircleRadius, minCircleRadius);maxCircleRadius = ta.getInt(R.styleable.CircleLoadingView_maxCircleRadius, maxCircleRadius);circleColor = ta.getColor(R.styleable.CircleLoadingView_circleColor, circleColor);ta.recycle();}paint = new Paint();paint.setColor(circleColor);//画笔颜色paint.setStyle(Paint.Style.FILL);//填充 绘制实心圆paint.setAntiAlias(true);//抗锯齿initAnimation();}
  • 自定义属性处理:通过 TypedArray 读取 XML 中设置的自定义属性(如圆的数量、颜色等),如果没设置则使用默认值
  • 画笔初始化:配置绘制圆形的画笔,设置为实心、抗锯齿
  • 动画初始化:调用 initAnimation() 方法创建旋转动画
1.4 动画实现
 // 创建旋转动画,3秒完成一圈,无限循环private void initAnimation() {rotateAnimation = new RotateAnimation(0, 360,Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);rotateAnimation.setDuration(3000);rotateAnimation.setRepeatCount(Animation.INFINITE);rotateAnimation.setInterpolator(new LinearInterpolator());startAnimation(rotateAnimation);}
1.5 绘制

这是自定义 View 的核心方法,负责绘制所有圆形:

 @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 获取视图中心坐标int centerX = getWidth() / 2;int centerY = getHeight() / 2;// 计算可用的半径(视图最小边的一半减去最大圆半径)int availableRadius = Math.min(centerX, centerY) - maxCircleRadius;// 绘制每个圆for (int i = 0; i < circleCount; i++) {// 计算每个圆的角度(等分圆周,从顶部开始)// 减去90度(Math.PI/2)使第一个点位于顶部float angle = (float) (2 * Math.PI * i / circleCount - Math.PI / 2);// 计算当前圆的半径(从小到大渐变)float radius = minCircleRadius;if (circleCount > 1) {radius = minCircleRadius + (maxCircleRadius - minCircleRadius) * (float) i / (circleCount - 1);}// 计算圆的中心点坐标(均匀分布在圆形轨迹上)float x = centerX + (float) (availableRadius * Math.cos(angle));float y = centerY + (float) (availableRadius * Math.sin(angle));// 设置画笔颜色(可选:可以根据位置设置不同的透明度)paint.setAlpha(calculateAlpha(i));// 绘制圆canvas.drawCircle(x, y, radius, paint);}}/*** 根据圆点位置计算透明度,创建渐变效果* @param index 圆点索引* @return 透明度值(0-255)*/private int calculateAlpha(int index) {// 可以根据需要调整透明度计算逻辑// 这里创建一个渐变效果,第一个点最暗,最后一个点较亮if (circleCount <= 1) return 255;// 计算透明度,return 155 + (index * 100 / (circleCount - 1));}
1.6: 尺寸测量
 @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 设置默认大小int defaultSize = dp2px(80);int width = measureSize(defaultSize, widthMeasureSpec);int height = measureSize(defaultSize, heightMeasureSpec);setMeasuredDimension(width, height);}private int measureSize(int defaultSize, int measureSpec) {int result = defaultSize;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if (specMode == MeasureSpec.EXACTLY) {result = specSize;} else if (specMode == MeasureSpec.AT_MOST) {result = Math.min(defaultSize, specSize);}return result;}// dp转pxprivate int dp2px(float dpValue) {final float scale = getContext().getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}
  • 处理 View 的尺寸测量,支持 wrap_contentmatch_parent 和固定尺寸
  • 默认大小为 80dp,通过 dp2px() 方法将 dp 转为 px,适配不同屏幕密度
1.7 : attrs自定义参数
<!-- 自定义加载视图的属性 -->
<declare-styleable name="CircleLoadingView"><attr name="circleCount" format="integer" /><attr name="minCircleRadius" format="integer" /><attr name="maxCircleRadius" format="integer" /><attr name="circleColor" format="color" />
</declare-styleable>

目前定义了四个属性包括圆点的个数,颜色,最大最小的半径. 有其他需求的我们可以继续补充.

下面是完整的自定义View的代码:

public class CircleLoadingView extends View {private static final String TAG = "CircleLoadingView";//默认参数private int circleCount = 8; // 圆圈数量private int minCircleRadius = 5; // 最小圆圈半径private int maxCircleRadius = 15; // 最大圆圈半径private int circleColor = Color.parseColor("#3F51B5"); //默认颜色// 旋转动画private RotateAnimation rotateAnimation;// 画笔private Paint paint;public CircleLoadingView(Context context) {super(context);init(null);}public CircleLoadingView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init(attrs);}public CircleLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(attrs);}private void init(AttributeSet attrs) {if (attrs != null){TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.CircleLoadingView);circleCount = ta.getInt(R.styleable.CircleLoadingView_circleCount, circleCount);minCircleRadius = ta.getInt(R.styleable.CircleLoadingView_minCircleRadius, minCircleRadius);maxCircleRadius = ta.getInt(R.styleable.CircleLoadingView_maxCircleRadius, maxCircleRadius);circleColor = ta.getColor(R.styleable.CircleLoadingView_circleColor, circleColor);ta.recycle();}paint = new Paint();paint.setColor(circleColor);//画笔颜色paint.setStyle(Paint.Style.FILL);//填充 绘制实心圆paint.setAntiAlias(true);//抗锯齿initAnimation();}// 创建旋转动画,3秒完成一圈,无限循环private void initAnimation() {rotateAnimation = new RotateAnimation(0, 360,Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);rotateAnimation.setDuration(3000);rotateAnimation.setRepeatCount(Animation.INFINITE);rotateAnimation.setInterpolator(new LinearInterpolator());startAnimation(rotateAnimation);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 获取视图中心坐标int centerX = getWidth() / 2;int centerY = getHeight() / 2;// 计算可用的半径(视图最小边的一半减去最大圆半径)int availableRadius = Math.min(centerX, centerY) - maxCircleRadius;// 绘制每个圆for (int i = 0; i < circleCount; i++) {// 计算每个圆的角度(等分圆周,从顶部开始)// 减去90度(Math.PI/2)使第一个点位于顶部float angle = (float) (2 * Math.PI * i / circleCount - Math.PI / 2);// 计算当前圆的半径(从小到大渐变)float radius = minCircleRadius;if (circleCount > 1) {radius = minCircleRadius + (maxCircleRadius - minCircleRadius) * (float) i / (circleCount - 1);}// 计算圆的中心点坐标(均匀分布在圆形轨迹上)float x = centerX + (float) (availableRadius * Math.cos(angle));float y = centerY + (float) (availableRadius * Math.sin(angle));// 设置画笔颜色(可选:可以根据位置设置不同的透明度)paint.setAlpha(calculateAlpha(i));// 绘制圆canvas.drawCircle(x, y, radius, paint);}}/*** 根据圆点位置计算透明度,创建渐变效果* @param index 圆点索引* @return 透明度值(0-255)*/private int calculateAlpha(int index) {// 可以根据需要调整透明度计算逻辑// 这里创建一个渐变效果,第一个点最暗,最后一个点较亮if (circleCount <= 1) return 255;// 计算透明度,return 155 + (index * 100 / (circleCount - 1));}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 设置默认大小int defaultSize = dp2px(80);int width = measureSize(defaultSize, widthMeasureSpec);int height = measureSize(defaultSize, heightMeasureSpec);setMeasuredDimension(width, height);}private int measureSize(int defaultSize, int measureSpec) {int result = defaultSize;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if (specMode == MeasureSpec.EXACTLY) {result = specSize;} else if (specMode == MeasureSpec.AT_MOST) {result = Math.min(defaultSize, specSize);}return result;}// dp转pxprivate int dp2px(float dpValue) {final float scale = getContext().getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}// 开始动画public void startLoading() {if (rotateAnimation != null && rotateAnimation.hasEnded()) {startAnimation(rotateAnimation);}}// 停止动画public void stopLoading() {if (rotateAnimation != null) {clearAnimation();}}// 更新圆点的颜色public void setCircleColor(int color) {this.circleColor = color;paint.setColor(color);invalidate();}}

二: 自定义Dialog.

2.1 代码

这里我写的很简单, 直接集成DIalog. 界面也只有CircleLoadingView.

public class CircleDialog extends Dialog {private static final String TAG = "CircleDialog";private CircleLoadingView loadingView;public CircleDialog(@NonNull Context context) {super(context, R.style.LoadingDialogStyle);}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_circle_dialog);loadingView = findViewById(R.id.loading_view);}private void initWindow() {Window window = getWindow();if (window != null) {// 设置窗口属性WindowManager.LayoutParams params = window.getAttributes();// 设置窗口居中params.gravity = Gravity.CENTER;// 设置窗口宽高为包裹内容params.width = WindowManager.LayoutParams.WRAP_CONTENT;params.height = WindowManager.LayoutParams.WRAP_CONTENT;// 设置窗口背景透明度params.dimAmount = 0.3f;window.setAttributes(params);// 设置窗口背景为透明window.setBackgroundDrawableResource(android.R.color.transparent);// 设置窗口是否有遮罩层window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);}// 设置点击外部是否可取消setCanceledOnTouchOutside(false);}// 设置加载圆圈的颜色public void setCircleColor(int color) {if (loadingView != null) {loadingView.setCircleColor(color);}}@Overridepublic void show() {super.show();if (loadingView != null) {loadingView.startLoading();}}@Overridepublic void dismiss() {super.dismiss();if (loadingView != null) {loadingView.stopLoading();}}
}
2.2: layout布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="vertical"><com.test.accessbilitytest.view.CircleLoadingViewandroid:layout_width="80dp"android:layout_height="80dp"android:id="@+id/loading_view"android:layout_gravity="center_horizontal"app:circleCount="8"app:minCircleRadius="10"app:maxCircleRadius="16"app:circleColor="#fffdbe32"/>
</LinearLayout>
2.3: dialog的样式
<!-- 加载弹窗样式 -->
<style name="LoadingDialogStyle" parent="@android:style/Theme.Dialog"><!-- 不显示标题栏 --><item name="android:windowNoTitle">true</item><!-- 背景透明 --><item name="android:windowBackground">@android:color/transparent</item><!-- 窗口进入退出动画 --><item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>

三: 弹框的特点.

    1.	核心是CircleLoadingView自定义视图,它负责绘制 8个从小到大的实心圆,并通过旋转动画实现周期性旋转效果。2.	通过自定义属性,您可以轻松调整:圆的数量(默认 8个)最小圆半径最大圆半径圆的颜色3.	LoadingDialog封装了弹窗的显示逻辑,包括设置弹窗样式、背景透明度等。4.	使用方法简单:创建LoadingDialog实例调用show()方法显示调用dismiss()方法隐藏

文章转载自:

http://bLhTlSaJ.zdtfr.cn
http://9HKEfM0i.zdtfr.cn
http://wNbZzbce.zdtfr.cn
http://hDiHPwSO.zdtfr.cn
http://OoErKB2i.zdtfr.cn
http://4l9BVJn9.zdtfr.cn
http://NWvicD5g.zdtfr.cn
http://n1B83DME.zdtfr.cn
http://wd85Fxe1.zdtfr.cn
http://QkMvf5cK.zdtfr.cn
http://FUwdGBY0.zdtfr.cn
http://D8rWzJm0.zdtfr.cn
http://4tQWSirP.zdtfr.cn
http://SlURPNR5.zdtfr.cn
http://hkfckCne.zdtfr.cn
http://zbHnwUcr.zdtfr.cn
http://wIyChgtP.zdtfr.cn
http://rfdy16cN.zdtfr.cn
http://d6rmbLZS.zdtfr.cn
http://JLqj4KWz.zdtfr.cn
http://aHTHHiAf.zdtfr.cn
http://KXGMj07T.zdtfr.cn
http://A6hNVgfl.zdtfr.cn
http://sZiHWoIk.zdtfr.cn
http://X29U8pIo.zdtfr.cn
http://TvI3Iq1V.zdtfr.cn
http://8EXKWNMC.zdtfr.cn
http://tT60BTQq.zdtfr.cn
http://fAaR0F1F.zdtfr.cn
http://tHkpSI9o.zdtfr.cn
http://www.dtcms.com/a/381917.html

相关文章:

  • 永磁同步电机无速度算法--改进滑模观测器(改进指数趋近律)
  • 【企业架构】TOGAF架构标准规范-架构规划
  • git常见冲突场景及解决办法
  • [code-review] 文件过滤逻辑 | 范围管理器
  • 学习嵌入式第五十三天
  • [code-review] 日志机制 | `LOG_LEVEL`
  • 物联网-无人自助茶室-如何实现24H智能营业?
  • JVM基础篇以及JVM内存泄漏诊断与分析
  • 【WRF数据准备】批量下载ERA5再分析数据-气象驱动数据
  • 如何实现文件批量重命名自动化
  • 【Unity 性能优化之路——概述(0)】
  • 零基础学AI大模型之SpringAI
  • AI行业应用:金融、医疗、教育、制造业的落地案例
  • 一文详解 Python 密码哈希库 Passlib
  • 360浏览器录屏功能、360浏览器录屏使用、免费录屏工具、Windows内置工具、开发者效率工具
  • 老梁聊全栈系列:现代全栈的「角色边界」与「能力雷达图」
  • ES——(三)DSL高级查询
  • 深度神经网络1——梯度问题+标签数不够问题
  • 【Unity UGUI 自动布局组(12)】
  • RAG 从入门到放弃?丐版 demo 实战笔记(go+python)
  • goland 断点调试显示“变量不可用”
  • Qt/C++,windows多进程demo
  • 再谈golang的sql链接dsn
  • pre-commit run --all-files 报错:http.client.RemoteDisconnected
  • STM32N6AI资料汇总
  • 【MySQL】E-R图
  • QT元对象系统(未完)
  • Netty 针对 Java NIO Selector 优化:SelectedSelectionKeySet
  • 抑制信号突变(模拟量采集+斜坡函数)
  • C语言入门指南:字符函数和字符串函数