Android Show ProgressBar Below Toolbar/AppBar

November 27, 2019

Layout

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.support.GalleryActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

            <ProgressBar
                android:id="@+id/progressBar"
                android:layout_marginTop="-7dp"
                android:layout_marginBottom="-7dp"
                android:max="100"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                style="?android:attr/progressBarStyleHorizontal"
                android:visibility="gone"
                />

    </com.google.android.material.appbar.AppBarLayout>

    <include layout="@layout/main_content" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

I am using LiveData to update ProgressBar, which support 2 mode

  • Busy Mode (horizontal animation)
  • Progress Mode (horizontal progress)
class MainActivity : AppCompatActivity() {
    private val viewModel by viewModels<MainViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
        setSupportActionBar(toolbar)

        init()
    }

    private fun init() {
        viewModel.busyEvent.observe(this, Observer { event ->
            event.getIfPending()?.also { show ->
                if (show) {
                    // prevent restart bug with progress animation
                    progressBar.progress = 0

                    progressBar.isVisible = true
                    progressBar.isIndeterminate = true
                }
                else {

                    progressBar.isGone = true
                    progressBar.isIndeterminate = false

                    // prevent restart bug with progress animation
                    progressBar.progress = 0
                }
            }
        })


        viewModel.progressEvent.observe(this, Observer { event ->
            event.getIfPending()?.also { value ->
                progressBar.isVisible = true
                progressBar.isIndeterminate = false
                val progress = (value * progressBar.max).toInt()

                /*
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    // progressBar.progress = progress
                    progressBar.setProgress(progress, true)
                }
                */

                val animation: ObjectAnimator =
                    ObjectAnimator.ofInt(progressBar, "progress", progressBar.progress, progress)
                animation.duration = 500
                animation.interpolator = DecelerateInterpolator()
                animation.start()
            }
        })
    }

    fun testBusy() {
        viewModel.busyEvent.value = Event(true)
        lifecycleScope.launch {
            delay(5000)
            viewModel.busyEvent.value = Event(false)
        }
    }

    fun testProgress() {
        viewModel.busyEvent.value = Event(true)
        lifecycleScope.launch {
            repeat(10) {
                delay(1000)
                viewModel.progressEvent.value = Event((it+1)/10.0)
            }
            viewModel.busyEvent.value = Event(false)
        }
    }
}

NOTE: Get ViewModel via ktx

ViewModel

class MainViewModel(application: Application) : AndroidViewModel(application) {
    internal val busyEvent = MutableLiveData<Event<Boolean>>()
    internal val progressEvent = MutableLiveData<Event<Double>>()
}

NOTE: Android LiveData: Handle UI Event With Event Wrapper

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.