androidandroid-layoutandroid-viewandroid-shape

filling a circle gradually from bottom to top android


I have created a circle with a stroke and white background using xml. How can this be filled gradually from bottom to top on user actions(e.g. on successive button press)?enter image description here

Is there any free library which can be used to achieve similar thing?


Solution

  • If you arrange your drawable like so:

    res/drawable/custom_progress.xml

    <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:paddingMode="stack">
    
        <item android:id="@android:id/background">
            <inset android:inset="@dimen/border_width">
                <shape android:shape="oval">
                    <solid android:color="@color/background" />
                </shape>
            </inset>
        </item>
    
        <item android:id="@android:id/progress">
            <inset android:inset="@dimen/border_width">
                <clip
                    android:clipOrientation="vertical"
                    android:gravity="bottom">
                    <shape android:shape="oval">
                        <solid android:color="@color/progress" />
                    </shape>
                </clip>
            </inset>
        </item>
    
        <item>
            <shape android:shape="oval">
                <stroke
                    android:width="@dimen/border_width"
                    android:color="@color/border" />
            </shape>
        </item>
    
    </layer-list>
    

    It can be used directly with a ProgressBar, like so:

    res/layout/activity_example.xml

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">
    
        <ProgressBar
            android:id="@+id/custom_progress_bar"
            style="?android:progressBarStyleHorizontal"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_marginVertical="10dp"
            android:progressDrawable="@drawable/custom_progress" />
    
        <com.google.android.material.slider.Slider
            android:id="@+id/slider"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:valueTo="100"
            app:labelBehavior="gone" />
    
    </LinearLayout>
    

    Then it can be wired up to that Slider like so:

    class ExampleActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            val ui = ActivityExampleBinding.inflate(layoutInflater)
            setContentView(ui.root)
    
            ui.slider.addOnChangeListener { _, value, _ ->
                ui.customProgressBar.progress = value.roundToInt()
            }
        }
    }
    

    And you'll get something like this:

    Screenshot of the given setup.

    The <item>s in custom_progress are just regular drawables that can be set up and configured like in any other context. For example, the <solid> inside the <shape> could be replaced with a <gradient>, like is shown on the relevant developer page.


    The previous versions of this answer used a bespoke custom View to do this. I wouldn't necessarily recommend that approach these days, now that I'm a little more familiar with things, but it does show a pretty straightforward example of custom attributes, if you should need that for your setup. ProgressBar could be subclassed to apply custom attributes to the progress drawable, though it'd take a bit of tedious work to find the actual drawables that are wrapped in InsetDrawables and ClipDrawables and added to a LayerDrawable (unless you'd want to instantiate them manually, of course).

    Starting with API level 24, the other option for custom attributes would be to create a Drawable subclass that overrides inflate() to process them, allowing it to be configured in drawable XML.

    <drawable xmlns:app="http://schemas.android.com/apk/res-auto"
        class="com.your.package.CustomDrawable"
        app:progressColor="@color/blue"
        … />
    

    You can consult the platform Drawable classes to see how inflate() should be implemented.