I need to create a ProgressBar in android like this:

I have the figma UI ready and already converted the drawables from svg to vector xml. But now I am stuck here to create the look of my ProgressBar like this. What I have done so far is
class CustomProgressBar(context: Context, attrs: AttributeSet) : View(context, attrs) {
private var progress: Int = 0 // Progress in percentage
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val batteryIcon: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_battery)
private val batteryBackground: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.progress_bar_grey)
private val batteryProgress: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.progress_bar_green)
private val rect = Rect()
fun setProgress(value: Int) {
progress = value.coerceIn(0, 100) // Ensure progress is within 0-100%
invalidate() // Request a redraw
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Draw battery background
canvas.drawBitmap(batteryBackground, null, rect, paint)
// Calculate width of the progress based on current progress
val progressWidth = (width * progress / 100f).roundToInt()
// Draw battery progress
rect.right = progressWidth
canvas.save()
canvas.clipRect(rect)
canvas.drawBitmap(batteryProgress, null, this.rect, paint)
canvas.restore()
// Draw the battery icon on top
// Adjust the position as per your design requirements
val iconLeft = width / 2 - batteryIcon.width / 2
val iconTop = height / 2 - batteryIcon.height / 2
canvas.drawBitmap(batteryIcon, iconLeft.toFloat(), iconTop.toFloat(), paint)
// Draw the percentage text
paint.color = Color.BLACK // Change as per your design
paint.textSize = 40f // Change as per your design
val text = "$progress%"
val textWidth = paint.measureText(text)
val textX = width / 2 - textWidth / 2
val textY = height / 2 - (paint.descent() + paint.ascent()) / 2
canvas.drawText(text, textX, textY, paint)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
// Update the rect for drawing background and progress
rect.set(0, 0, w, h)
}
}
then I created layer-list like this:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/background">
<bitmap android:src="@drawable/progress_bar_grey" />
</item>
<item android:id="@+id/progress">
<clip
android:clipOrientation="horizontal"
android:gravity="left">
<bitmap android:src="@drawable/progress_bar_green" />
</clip>
</item>
</layer-list>
when I take this progressbar in my Activity I am getting nullpointer exception in
private val batteryIcon: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_battery)
the error report is:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.volarious.customviews/com.volarious.customviews.MainActivity}: android.view.InflateException: Binary XML file line #13 in com.volarious.customviews:layout/activity_main: Binary XML file line #13 in com.volarious.customviews:layout/activity_main: Error inflating class com.volarious.customviews.CustomProgressBar
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3822)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3963)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2468)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8248)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Caused by: android.view.InflateException: Binary XML file line #13 in com.volarious.customviews:layout/activity_main: Binary XML file line #13 in com.volarious.customviews:layout/activity_main: Error inflating class com.volarious.customviews.CustomProgressBar
Caused by: android.view.InflateException: Binary XML file line #13 in com.volarious.customviews:layout/activity_main: Error inflating class com.volarious.customviews.CustomProgressBar
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at android.view.LayoutInflater.createView(LayoutInflater.java:866)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1018)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:973)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1135)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1096)
at android.view.LayoutInflater.inflate(LayoutInflater.java:694)
at android.view.LayoutInflater.inflate(LayoutInflater.java:538)
at android.view.LayoutInflater.inflate(LayoutInflater.java:485)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:775)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:197)
at com.volarious.customviews.MainActivity.onCreate(MainActivity.kt:11)
at android.app.Activity.performCreate(Activity.java:8621)
at android.app.Activity.performCreate(Activity.java:8599)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1456)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3804)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3963)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2468)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8248)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
2024-01-31 16:18:48.259 13110-13110 AndroidRuntime com.volarious.customviews E Caused by: java.lang.NullPointerException: decodeResource(...) must not be null
at com.volarious.customviews.CustomProgressBar.<init>(CustomProgressBar.kt:24)
... 29 more
can anyone help me to provide a clear guidance what I am missing and what I can do?
Since you are using a custom
Viewto draw, you don't need<layer-list>for progress (your view is not a progress, is custom)When drawing bitmaps with canvas, it has to be png. Set your bitmaps (ic_battery, progress_bar_grey, progress_bar_green) as pngs
then, in order to clip progress bitmap, you need an arc instead of rect: Modify
onDraw()to:check your start & end angles in figma, set to
figmaStartAngleandfigmaStartToEndAnglein order to have an accurate measurementFor the thumb: you can have a separate bitmap and draw into the sweep point