You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android中使用Java实现带时间动画的半圆弧进度条?

嘿,这个半圆弧进度条的需求我之前做过好几个类似的,咱们用自定义View就能完美实现,还能加上流畅的动画效果~下面一步步来拆解实现过程:

实现思路概述

核心思路是自定义View,通过Canvas绘制背景半圆弧和进度半圆弧,再用ValueAnimator驱动进度值的平滑变化,从而实现动画效果。具体分为这几步:

  • 初始化画笔和基础属性
  • 测量View尺寸,确保半圆比例协调
  • 绘制背景圆弧和进度圆弧
  • 添加动画逻辑,驱动进度更新
自定义半圆弧进度条View

直接上Java代码,关键部分我会做注释说明:

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

public class SemiCircleProgressBar extends View {
    // 背景圆弧画笔
    private Paint bgPaint;
    // 进度圆弧画笔
    private Paint progressPaint;
    // 圆弧所在的矩形区域
    private RectF arcRect;
    // 圆弧宽度(默认30dp)
    private int arcWidth = 30;
    // 背景圆弧颜色(默认浅灰色)
    private int bgColor = 0xFFE0E0E0;
    // 进度圆弧颜色(默认蓝色)
    private int progressColor = 0xFF2196F3;
    // 当前进度(范围0-100)
    private float currentProgress = 0;
    // 目标进度
    private float targetProgress = 0;

    public SemiCircleProgressBar(Context context) {
        super(context);
        init();
    }

    public SemiCircleProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public SemiCircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        // 初始化背景画笔:抗锯齿、描边样式、圆角端点
        bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        bgPaint.setColor(bgColor);
        bgPaint.setStyle(Paint.Style.STROKE);
        bgPaint.setStrokeWidth(arcWidth);
        bgPaint.setStrokeCap(Paint.Cap.ROUND);

        // 初始化进度画笔,和背景画笔属性一致,只改颜色
        progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        progressPaint.setColor(progressColor);
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeWidth(arcWidth);
        progressPaint.setStrokeCap(Paint.Cap.ROUND);

        arcRect = new RectF();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 让View高度最多为宽度的一半,保证半圆比例协调
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = Math.min(MeasureSpec.getSize(heightMeasureSpec), width / 2);
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 计算圆弧矩形区域:留出圆弧宽度的一半,避免画笔超出View边界
        float padding = arcWidth / 2f;
        // 把矩形高度设为View高度的2倍,让半圆的圆心在View底部中间,画出上半圆
        arcRect.set(padding, padding, w - padding, h * 2 - padding);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 绘制背景半圆弧:从180度(左侧)开始,逆时针画180度(形成上半圆)
        canvas.drawArc(arcRect, 180, -180, false, bgPaint);

        // 根据当前进度计算进度圆弧的扫过角度
        float sweepAngle = -180 * (currentProgress / 100f);
        // 绘制进度圆弧
        canvas.drawArc(arcRect, 180, sweepAngle, false, progressPaint);
    }

    // 设置目标进度并启动动画
    public void setProgressWithAnimation(float target) {
        // 限制进度在0-100之间
        targetProgress = Math.max(0, Math.min(100, target));
        // 创建值动画,从当前进度平滑过渡到目标进度
        ValueAnimator animator = ValueAnimator.ofFloat(currentProgress, targetProgress);
        animator.setDuration(1500); // 动画时长1.5秒,可按需调整
        animator.addUpdateListener(animation -> {
            currentProgress = (float) animation.getAnimatedValue();
            invalidate(); // 刷新界面,触发重绘
        });
        animator.start();
    }

    // 以下是自定义属性的Setter方法,方便外部修改样式
    public void setArcWidth(int arcWidth) {
        this.arcWidth = arcWidth;
        bgPaint.setStrokeWidth(arcWidth);
        progressPaint.setStrokeWidth(arcWidth);
        invalidate();
    }

    public void setBgColor(int bgColor) {
        this.bgColor = bgColor;
        bgPaint.setColor(bgColor);
        invalidate();
    }

    public void setProgressColor(int progressColor) {
        this.progressColor = progressColor;
        progressPaint.setColor(progressColor);
        invalidate();
    }
}
在项目中使用这个View

1. 布局文件中添加View

在你的xml布局里引入自定义View(注意替换com.yourpackage为你的实际包名):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <com.yourpackage.SemiCircleProgressBar
        android:id="@+id/progressBar"
        android:layout_width="300dp"
        android:layout_height="150dp"
        android:layout_marginBottom="20dp"/>

    <Button
        android:id="@+id/btnStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动进度动画"/>

</LinearLayout>

2. 在Activity中触发动画

在Activity里绑定View,点击按钮启动进度动画:

import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private SemiCircleProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        progressBar = findViewById(R.id.progressBar);
        findViewById(R.id.btnStart).setOnClickListener(v -> {
            // 设置目标进度为75%,启动动画
            progressBar.setProgressWithAnimation(75);
        });
    }
}
扩展优化建议
  • 如果需要下半圆:修改onDraw中的drawArc参数,背景圆弧用startAngle=0, sweepAngle=180,进度圆弧的sweepAngle=180*(currentProgress/100f),同时调整arcRect的高度设置即可。
  • 添加进度文本:在onDraw中用canvas.drawText绘制当前进度百分比,注意计算文本的位置居中。
  • 自定义动画插值器:可以给ValueAnimator设置animator.setInterpolator(new AccelerateDecelerateInterpolator()),让动画节奏更自然。

内容的提问来源于stack exchange,提问作者Abdur Rehman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:27:43