如何基于Material规范实现非自定义Drawable的圆形确定式ProgressBar
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.
Option 1: Use Material Components Library (Recommended)
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

