如何在Android xml中自定义ProgressBar?

问题描述 投票:0回答:1

我需要在android中创建一个ProgressBar,如下所示:

我已经准备好了 Figma UI,并且已经将可绘制对象从 svg 转换为矢量 xml。但现在我被困在这里创建我的 ProgressBar 像这样的外观。到目前为止我所做的是

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)
    }
}

然后我创建了这样的图层列表:

<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>

当我在 Activity 中使用此进度条时,我在

中遇到空指针异常
private val batteryIcon: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_battery)

错误报告为:

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

任何人都可以帮助我提供明确的指导,我缺少什么以及我能做什么?

android progress-bar android-progressbar
1个回答
0
投票

由于您使用自定义

View
进行绘制,因此不需要
<layer-list>
来获取进度(您的视图不是进度,而是自定义的)

用canvas绘制位图时,它必须是png。将位图(ic_battery、progress_bar_grey、progress_bar_green)设置为 pngs

然后,为了剪辑进度位图,您需要一个圆弧而不是矩形: 将

onDraw()
修改为:

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    // Draw battery background
    canvas.drawBitmap(batteryBackground, null, rect, paint)

    val figmaStartAngle = 35f// 35f will be the angle of bitmap from 0 label (get from figma/designer)
    val figmaStartToEndAngle = 70f// 70f will be angle between label:0 to:100 of your bitmap in figma
    // canvas starts from right, rotate+90deg to start from bottom
    val startAngle = 90f + figmaStartAngle
    val endAngle = 360f - figmaStartToEndAngle
    // convert 0,100 to 0deg,360deg
    // in your case to bitmap start percentage to end percentage
    val sweepAngle = (progress / 100f) * endAngle

    val path = Path() // declare in top level class
    path.moveTo(width / 2f, height / 2f)
    path.lineTo(0f, height.toFloat())
    path.arcTo(0f, 0f, width.toFloat(), height.toFloat(), startAngle, sweepAngle, true)
    path.lineTo(width / 2f, height / 2f)

    canvas.save()
    canvas.clipPath(path)
    canvas.drawBitmap(batteryProgress, null, this.rect, paint)
    canvas.restore()

    // if you want to test clip arc
    // canvas.drawPath(path, paint)


    // 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 * .7f - (paint.descent() + paint.ascent()) / 2
    canvas.drawText(text, textX, textY, paint)
}

检查figma中的开始和结束角度,设置为

figmaStartAngle
figmaStartToEndAngle
以便进行准确的测量

对于拇指:您可以有一个单独的位图并绘制到扫描点

© www.soinside.com 2019 - 2024. All rights reserved.