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

如何基于Material规范实现非自定义Drawable的圆形确定式ProgressBar

Solution for Determinate Circular ProgressBar (Material Design)

Hey there, I totally get the frustration—native Android’s default ProgressBar doesn’t support a determinate circular variant out of the box, even though Material Design clearly defines it. Let’s walk through the best ways to fix this:

Why Your Initial Approach Didn’t Work

The default circular ProgressBar styles (like @android:style/Widget.ProgressBar.Normal) are hardcoded for indeterminate spinning animations. Setting android:indeterminate="false" and android:progress does nothing because the underlying Drawable doesn’t have logic to render progress updates.

If you’re already using Material Design in your app, the easiest solution is to use the CircularProgressIndicator from the Material Components library. It’s purpose-built for this exact use case and follows Material Design guidelines perfectly.

First, make sure you have the Material Components dependency in your module-level build.gradle:

implementation 'com.google.android.material:material:1.9.0' // Use the latest version available

Then add the indicator to your layout:

<com.google.android.material.progressindicator.CircularProgressIndicator
    android:id="@+id/circularDeterminateProgress"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:progress="50" <!-- Set initial progress (0-100 range) -->
    app:indicatorColor="@color/your_primary_color"
    app:trackColor="@color/your_light_gray_color"
    app:indicatorSize="48dp" <!-- Customize size as needed -->
    app:strokeWidth="4dp"/> <!-- Adjust stroke thickness -->

To update progress programmatically (with smooth animation):

val progressIndicator = findViewById<CircularProgressIndicator>(R.id.circularDeterminateProgress)
progressIndicator.setProgressCompat(75, true)

Option 2: Custom Drawable (For Native SDK Only)

If you can’t use Material Components, you’ll need to create a custom Drawable to handle the circular progress rendering. Here’s a simple implementation that aligns with Material Design:

Step 1: Create the Custom Drawable Class

import android.graphics.*
import android.graphics.drawable.Drawable
import android.view.View

class CircularDeterminateProgressDrawable : Drawable() {
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        style = Paint.Style.STROKE
        strokeWidth = 4.dpToPx()
        strokeCap = Paint.Cap.ROUND // Rounded ends for Material Design look
    }
    private var currentProgress = 0
    private val startAngle = -90f // Start drawing from the top of the circle

    override fun onDraw(canvas: Canvas) {
        val bounds = bounds
        val centerX = bounds.exactCenterX()
        val centerY = bounds.exactCenterY()
        val radius = min(centerX, centerY) - paint.strokeWidth / 2

        // Draw background track
        paint.color = Color.parseColor("#E0E0E0")
        canvas.drawCircle(centerX, centerY, radius, paint)

        // Draw progress arc
        paint.color = Color.parseColor("#2196F3") // Replace with your primary color
        val sweepAngle = (currentProgress / 100f) * 360f
        canvas.drawArc(
            centerX - radius,
            centerY - radius,
            centerX + radius,
            centerY + radius,
            startAngle,
            sweepAngle,
            false,
            paint
        )
    }

    fun setProgress(progress: Int) {
        currentProgress = progress.coerceIn(0, 100)
        invalidateSelf() // Trigger a redraw to update the progress
    }

    private fun Int.dpToPx(): Float {
        return this * resources.displayMetrics.density
    }

    override fun setAlpha(alpha: Int) {
        paint.alpha = alpha
    }

    override fun setColorFilter(colorFilter: ColorFilter?) {
        paint.colorFilter = colorFilter
    }

    override fun getOpacity(): Int {
        return PixelFormat.TRANSLUCENT
    }
}

Step 2: Use the Drawable in Your Layout/Code

Add a regular ProgressBar to your layout:

<ProgressBar
    android:id="@+id/customCircularProgress"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:indeterminate="false"/>

Then set the custom Drawable in your Activity/Fragment:

val progressBar = findViewById<ProgressBar>(R.id.customCircularProgress)
val customDrawable = CircularDeterminateProgressDrawable()
progressBar.indeterminateDrawable = customDrawable

// Update progress later in your logic
customDrawable.setProgress(60)

Final Notes

  • The Material Components approach is always preferred because it automatically handles accessibility, smooth animations, and theme consistency.
  • If you go the custom Drawable route, you can extend it to add custom animations, color states, or size adjustments easily.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:52:18