我需要在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
任何人都可以帮助我提供明确的指导,我缺少什么以及我能做什么?
由于您使用自定义
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
以便进行准确的测量
对于拇指:您可以有一个单独的位图并绘制到扫描点