【问题标题】:How does XML modify a horizontal progress bar to a circular one with progressDrawable property?XML 如何将水平进度条修改为具有progressDrawable 属性的圆形进度条?
【发布时间】:2023-12-21 12:59:01
【问题描述】:

我有这个ProgressBarxml:

<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>
  • 该可绘制文件如何将水平物体变为圆形物体,以及当进度为 100% 时,它如何决定如何将进度条从 0 度设置为 360 度以形成一个完整的环?另外,有没有办法控制动画的开始(比如从正 x 轴成 90 度)?
  • 如何制作另一个自定义形状,例如从下到上填充不改变背景颜色的矩形(在填充之前表示矩形形状)?

【问题讨论】:

  • 一些问题已经得到解答,例如:(操纵动画的开始)*.com/questions/23237564/… 等等...
  • @Rifat 我的其他问题呢(比如它如何决定如何将 ProgressBar 绘制到一个环上)?
  • 我发现其他搜索结果以某种方式提供了解决方案。 ProgressBar 形状也依赖于 xml 属性。对于矩形类型的进度条,您可以使用水平方向,如果您只想显示它正在填充,您可以将背景设置为不可见,并将进度条设置为可见。
  • @Rifat 您能否为我的所有问题提供完整的描述性答案?

标签: android android-drawable android-progressbar xml-drawable


【解决方案1】:
  • 该可绘制文件如何将水平物体变为圆形物体,以及当进度为 100% 时,它如何决定如何将进度条从 0 度设置为 360 度以形成一个完整的环?

当您指定style="?android:progressBarStyleHorizontal" 时,您真正想说的是 ProgressBar 将是确定的。来自 ProgressBardocumentation

为了表示确定的进度,您将进度条的样式设置为R.style.Widget_ProgressBar_Horizontal 并设置进度量。

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

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

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

  • 另外,有没有办法控制动画的开始(例如从正 x 轴 90 度)?

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

  • 如何制作另一个自定义形状,例如从下到上填充不改变背景颜色的矩形(在填充之前表示矩形形状)?

有几种(也许更多)方法可以做到这一点。一种方法是定义一个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()
    }
}

【讨论】:

  • 完美,所以是 useLevel 告诉 XML 如何绘制进度条。很深入的解释!但是,除了developer.android.com/guide/topics/resources/drawable-resource 之外,还有没有更深入的解释来解释 useLevel 的工作原理?
  • @Richard 不确定在线是否有深入讨论。如果您想了解详细信息,请尝试从 Drawable.java 中的 onLevelChange() 方法开始,然后从那里开始工作。
  • 好的,有没有说明进度条如何依赖于drawables 项目的id 的文档?直到你提到它我才知道。此外,id 是否必须完全是 @android:id/progress 才能根据其 level 进行动画处理?如果没有,是否有可能我可以使用单个进度条同时为两个 progresses 设置动画?
最近更新 更多