XML如何使用progressDrawable属性将水平进度条修改为圆形进度条?

How does XML modify a horizontal progress bar to a circular one with progressDrawable property?

我有这个 ProgressBar xml:

<ProgressBar
  android:id="@+id/progressBar"
  style="?android:progressBarStyleHorizontal"
  android:layout_width="80dp"
  android:layout_height="80dp"
  android:layout_marginBottom="32dp"
  android:progress="0"
  android:progressDrawable="@drawable/bg_circular_progress_bar"
  app:layout_constraintBottom_toBottomOf="parent"
  app:layout_constraintEnd_toEndOf="parent"
  app:layout_constraintStart_toStartOf="parent"
  app:layout_constraintTop_toTopOf="parent"
  android:elevation="11dp"/>

可以看出,style="?android:progressBarStyleHorizontal"指定它是一个单杠。但是,对于 progressDrawable 属性,我可以通过以下可绘制文件以某种方式将其形状更改为圆形:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="ring"
    android:innerRadiusRatio="2.5"
    android:thickness="4dp"
    android:useLevel="true">

    <solid android:color="@color/colorPrimary" />
</shape>
  • How does that drawable file change a horizontal thing into a circular thing and how does it decide how to animate the progress bar from 0degrees to 360degress forming a full ring when the progress is 100%?

当您指定 style="?android:progressBarStyleHorizontal" 时,您实际上是在说 ProgressBar 将是确定的。从 documentation for ProgressBar:

To indicate determinate progress, you set the style of the progress bar to R.style.Widget_ProgressBar_Horizontal and set the amount of progress.

默认情况下,ProgressBar 是水平的,但正如您所指出的,可以使用您的可绘制文件将其更改为圆形。

<shape 
    android:shape="ring"
    android:innerRadiusRatio="2.5"
    android:thickness="4dp"
    android:useLevel="true">
    <solid android:color="@color/colorPrimary" />
</shape>

注意行 android:useLevel="true"。这指定 drawable 可以接受范围从 0 到 100 的 level。drawable 知道根据级别集绘制多少自身:0 是不绘制,100 是绘制 100%。通过更改进度,您正在更改为可绘制对象设置的级别。尝试设置 android:useLevel="false" 看看会发生什么。

  • Also, is there a way to manipulate the start of the animation (like from 90degrees from the positive x-axis)?

在 XML 中,设置 android:rotation="90" 使环从底部开始。

  • How do I make another custom shape, like a rectangle being filled from bottom to top with a background color (to signify the rectangle shape before being filled) that doesn't change?

有几种(也许更多)方法可以做到这一点。一种方法是定义一个 layer-list drawable 来定义背景矩形和可绘制比例的进度矩形。由于 ProgressBar 依赖于它们,因此必须指定 ID。

<layer-list>
<item
    android:id="@android:id/background">
    <shape android:shape="rectangle">
        <solid android:color="@android:color/darker_gray" />
    </shape>
</item>
<item
    android:id="@android:id/progress">
    <scale
        android:scaleHeight="100%"
        android:scaleGravity="bottom">
        <shape android:shape="rectangle">
            <solid android:color="@android:color/holo_red_light" />
        </shape>
    </scale>
</item>

您还可以在图层列表中使用剪辑可绘制对象:

<layer-list>
    <item android:id="@android:id/background">
        <shape android:shape="rectangle">
            <solid android:color="@android:color/darker_gray" />
        </shape>
    </item>
    <item android:id="@android:id/progress">
        <clip
            android:clipOrientation="vertical"
            android:gravity="bottom">
            <shape android:shape="rectangle">
                <solid android:color="@android:color/holo_red_light" />
            </shape>
        </clip>
    </item>
</layer-list>

这是一个将所有这些放在一起的示例布局:

activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:progressBarStyleHorizontal"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_marginTop="16dp"
        android:progress="0"
        android:progressDrawable="@drawable/bg_circular_progress_bar"
        android:rotation="90"
        app:layout_constraintVertical_chainStyle="packed"
        app:layout_constraintBottom_toTopOf="@+id/progressBar2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ProgressBar
        android:id="@+id/progressBar2"
        style="?android:progressBarStyleHorizontal"
        android:layout_width="20dp"
        android:layout_height="80dp"
        android:layout_marginTop="16dp"
        android:progress="0"
        android:progressDrawable="@drawable/rectangular_progress_with_scale_drawable"
        app:layout_constraintBottom_toTopOf="@+id/progressBar3"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar" />

    <ProgressBar
        android:id="@+id/progressBar3"
        style="?android:progressBarStyleHorizontal"
        android:layout_width="20dp"
        android:layout_height="80dp"
        android:layout_marginTop="16dp"
        android:progress="0"
        android:progressDrawable="@drawable/rectangular_progress_with_clip_drawable"
        app:layout_constraintBottom_toTopOf="@+id/textView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar2" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="0/ 100"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar3" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:onClick="onClick"
        android:text="Start"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun onClick(view: View) {
        view.isEnabled = false
        var progress = 0
        val handler = Handler()

        Thread(Runnable {
            while (progress < 100) {
                progress += 5
                handler.post {
                    progressBar.progress = progress
                    progressBar2.progress = progress
                    progressBar3.progress = progress
                    textView.text = "$progress/ ${progressBar.max}"
                }
                try {
                    Thread.sleep(100)
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }
            }
            runOnUiThread { view.isEnabled = true }
        }).start()
    }
}