news 2026/6/10 18:48:23

Android 两种进度条(滚动进度条、循环进度条)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android 两种进度条(滚动进度条、循环进度条)

一、滚动进度条

<stylename="CustomProgressBar.Horizontal"parent="@android:style/Widget.ProgressBar.Horizontal"><itemname="android:indeterminateOnly">false</item><itemname="android:progressDrawable">@android:drawable/progress_horizontal</item><!-- 确定模式 --><itemname="android:indeterminateDrawable">@drawable/progress_indeterminate_horizontal</item><!-- 你的 animation-list --><itemname="minHeight">20dp</item><itemname="maxHeight">20dp</item><itemname="android:mirrorForRtl">true</item><itemname="android:padding">2dp</item></style>

progress_indeterminate_horizontal文件如下:

<?xmlversion="1.0"encoding="utf-8"?><animation-list xmlns:android="http://schemas.android.com/apk/res/android"android:oneshot="false"><item android:drawable="@drawable/progressbar_indeterminate1"android:duration="200"/><item android:drawable="@drawable/progressbar_indeterminate2"android:duration="200"/><item android:drawable="@drawable/progressbar_indeterminate3"android:duration="200"/></animation-list>

progressbar_indeterminate1、progressbar_indeterminate2、progressbar_indeterminate3分别如下:

<vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="36dp"android:height="30dp"android:viewportWidth="36"android:viewportHeight="30"><group><clip-path android:pathData="M0,0h36v30h-36z"/><path android:pathData="M0,0h36v30h-36z"android:fillColor="#CBCBCB"/><path android:pathData="M24,41L47,18"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/><path android:pathData="M-3,14L14,-3"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/><path android:pathData="M-2.5,32L33.5,-4"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/><path android:pathData="M16,32L39,9"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/></group></vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="36dp"android:height="30dp"android:viewportWidth="36"android:viewportHeight="30"><group><clip-path android:pathData="M0,0h36v30h-36z"/><path android:pathData="M0,0h36v30h-36z"android:fillColor="#CBCBCB"/><path android:pathData="M14,39L42,11.5"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/><path android:pathData="M-10,11L7,-6"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/><path android:pathData="M-9,27L27,-9"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/><path android:pathData="M-1.5,37L38,-3"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/></group></vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="36dp"android:height="30dp"android:viewportWidth="36"android:viewportHeight="30"><group><clip-path android:pathData="M0,0h36v30h-36z"/><path android:pathData="M0,0h36v30h-36z"android:fillColor="#CBCBCB"/><path android:pathData="M19,39L47,11.5"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/><path android:pathData="M-5,11L12,-6"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/><path android:pathData="M-3,27L33,-9"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/><path android:pathData="M4.25,37L43.75,-3"android:strokeWidth="6"android:fillColor="#00000000"android:strokeColor="#ab0b0b"/></group></vector>

使用:

<ProgressBar android:id="@+id/progressBar"style="@style/CustomProgressBar.Horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:indeterminate="true"/>

二、循环进度条

package com.android.simple.utils;importandroid.animation.ValueAnimator;importandroid.content.Context;importandroid.graphics.Canvas;importandroid.graphics.Color;importandroid.graphics.Paint;importandroid.graphics.RectF;importandroid.util.AttributeSet;importandroid.util.Log;importandroid.view.View;importandroid.view.animation.LinearInterpolator;public class MovingLineProgressBar extends View{private static final String TAG="MovingLineProgressBar";private float progressBarWidth=0f;// 进度条总宽度 private float lineWidth=0f;// 移动线条宽度 private float currentPosition=0f;// 当前线条位置 private Paint backgroundPaint=new Paint(Paint.ANTI_ALIAS_FLAG);private Paint linePaint=new Paint(Paint.ANTI_ALIAS_FLAG);private ValueAnimator animator;public MovingLineProgressBar(Context context){super(context);init();}public MovingLineProgressBar(Context context, AttributeSet attrs){super(context, attrs);init();}public MovingLineProgressBar(Context context, AttributeSet attrs, int defStyleAttr){super(context, attrs, defStyleAttr);init();}private voidinit(){// 初始化画笔(不变) backgroundPaint.setColor(Color.WHITE);backgroundPaint.setStyle(Paint.Style.STROKE);backgroundPaint.setStrokeWidth(8f);linePaint.setColor(Color.RED);linePaint.setStyle(Paint.Style.STROKE);linePaint.setStrokeWidth(8f);lineWidth=100f;setupAnimator();}@Override protected void onSizeChanged(int w, int h, int oldw, int oldh){super.onSizeChanged(w, h, oldw, oldh);progressBarWidth=w - lineWidth;// 注意:w 是 int,转 float // Log.d(TAG,"onSizeChanged: width = "+ w +", progressBarWidth = "+ progressBarWidth);// 如果动画已启动,但宽度变化了,重置动画if(animator!=null&&animator.isStarted()){animator.cancel();currentPosition=0f;// 重置位置 startAnimation();// 重新启动}}@Override protected void onDraw(Canvas canvas){super.onDraw(canvas);if(canvas==null)return;float centerY=getHeight()/ 2f;float left=0f;float right=getWidth();// 绘制背景进度条(不变) RectF backgroundRect=new RectF(left, centerY - 4f, right, centerY + 4f);canvas.drawRoundRect(backgroundRect, 4f, 4f, backgroundPaint);// 绘制移动线条(不变) float lineLeft=currentPosition;float lineRight=currentPosition + lineWidth;RectF lineRect=new RectF(lineLeft, centerY - 4f, lineRight, centerY + 4f);canvas.drawRoundRect(lineRect, 4f, 4f, linePaint);// Log.d(TAG,"onDraw: currentPosition = "+ currentPosition);// 调试日志}private voidsetupAnimator(){animator=ValueAnimator.ofFloat(0f, progressBarWidth);// 注意:初始时progressBarWidth=0animator.setDuration(2000L);//每轮动画持续2秒 animator.setInterpolator(new LinearInterpolator());//线性插值,确保匀速移动 animator.setRepeatCount(ValueAnimator.INFINITE);//无限重复 animator.setRepeatMode(ValueAnimator.RESTART);//每次重复从头开始(回到0位置) animator.addUpdateListener(animation ->{currentPosition=(Float)animation.getAnimatedValue();invalidate();// 可选:添加日志查看动画更新(生产环境移除) // Log.d(TAG,"Animation update: position = "+ currentPosition);});}@Override protected voidonAttachedToWindow(){super.onAttachedToWindow();// 注释掉自动启动,避免与手动启动冲突 // startAnimation();Log.d(TAG,"onAttachedToWindow: View attached");}@Override protected voidonDetachedFromWindow(){super.onDetachedFromWindow();if(animator!=null){animator.cancel();}Log.d(TAG,"onDetachedFromWindow: Animation stopped");}// 公共方法:开始动画(增强版) public voidstartAnimation(){if(animator==null){Log.e(TAG,"Animator is null, cannot start");return;}// 检查宽度是否有效if(progressBarWidth<=0){Log.w(TAG,"progressBarWidth <= 0 ("+ progressBarWidth +"), delaying start until layout");// 延迟启动,直到布局完成 post(()->{ if(progressBarWidth>0){ startAnimation();} else { Log.e(TAG,"Still invalid width after post,check layout XML");} });return;}//如果已启动,先取消 if(animator.isStarted()){animator.cancel();currentPosition=0f;// 重置位置}// 更新动画结束值(因为 ofFloat 是初始设置的) animator.setFloatValues(0f, progressBarWidth);animator.start();Log.d(TAG,"Animation started with width: "+ progressBarWidth);}public voidstopAnimation(){if(animator!=null){animator.cancel();currentPosition=0f;// 重置位置 invalidate();// 立即重绘到初始状态 Log.d(TAG,"Animation stopped");}}}

使用:

binding.progressBar.startAnimation();
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 11:14:48

没预算怎么玩ResNet18?云端GPU 1小时1块,随用随付

没预算怎么玩ResNet18&#xff1f;云端GPU 1小时1块&#xff0c;随用随付 1. 为什么大学生创客需要ResNet18&#xff1f; 作为一名经历过学生时代的技术老兵&#xff0c;我完全理解大学生团队想做智能垃圾分类却苦于没有GPU资源的困境。ResNet18作为深度学习领域的"入门…

作者头像 李华
网站建设 2026/6/10 12:56:06

无需测试环境!如何利用测试脚手架隔离微服务,实现功能自动化

以下为作者观点&#xff1a; 想在不建立完整测试环境的情况下测试微服务&#xff1f; 想在将变更推送到主线分支之前完成测试&#xff1f; 这是我们在进行项目交付时经常遇到的难题。最近&#xff0c;当我们开始一个新的项目&#xff0c;为客户构建一个新的聚合平台时&#…

作者头像 李华
网站建设 2026/6/10 13:00:04

MiDaS模型解析:轻量化设计的背后技术

MiDaS模型解析&#xff1a;轻量化设计的背后技术 1. 引言&#xff1a;AI 单目深度估计的现实意义 在计算机视觉领域&#xff0c;从单张2D图像中恢复3D空间结构一直是极具挑战性的任务。传统方法依赖多视角几何或激光雷达等硬件设备&#xff0c;成本高且部署复杂。而近年来&am…

作者头像 李华
网站建设 2026/5/31 14:11:45

网络信息安全工程师证2026年如何报考?了解这几点让你轻松考证!收藏这一篇就够了

网络信息安全工程师是一种专门从事网络安全工作的职业。随着互联网的快速发展和普及&#xff0c;网络安全问题也日益突出&#xff0c;因此网络信息安全工程师的需求也越来越大。 网络信息安全工程师主要负责保护网络系统和数据的安全&#xff0c;防止黑客攻击、病毒侵入、数据泄…

作者头像 李华
网站建设 2026/6/10 1:04:31

Qwen2.5-7B + vLLM:离线批量推理的高效落地方案

Qwen2.5-7B vLLM&#xff1a;离线批量推理的高效落地方案 在大模型应用日益普及的今天&#xff0c;如何在有限资源下实现高性能、低成本的推理服务成为工程落地的关键挑战。尤其在数据处理密集型场景中&#xff0c;离线批量推理&#xff08;Offline Batch Inference&#xff…

作者头像 李华