MVVM notify View about loading state

you can use DataBinding for that too make a separate layout to reuse it everywhere laoding_state_xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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">

    <View
        android:id="@+id/view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ProgressBar
        android:id="@+id/progressBar2"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> 

then include it inside your desired layout

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

        <import type="android.view.View" />

        <variable
            name="viewModel"
            type="com.blogspot.soyamr.notforgotagain.view.signin.SignInViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".view.signin.SignInFragment">

        <include
            android:id="@+id/include"
            layout="@layout/toolbar_application"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/signInButtonView"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginTop="68dp"
            android:layout_marginEnd="16dp"
            android:onClick="@{() -> viewModel.logIn()}"
            android:text="@string/sign_in"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/passwordTextInputLayout" />

        <TextView
            android:id="@+id/noAccountTextview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="24dp"
            android:layout_marginEnd="4dp"
            android:text="@string/no_account"
            android:textColor="@android:color/black"
            app:layout_constraintEnd_toStartOf="@+id/createAccountTextView"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintHorizontal_chainStyle="packed"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/signInButtonView" />

        <com.google.android.material.textfield.TextInputLayout
            android:id="@+id/emailTextInputLayout"
            style="@style/myTextInputLayoutStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginTop="80dp"
            android:layout_marginEnd="16dp"
            app:errorEnabled="true"
            app:errorText="@{viewModel.emailErrorMessage}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/include">

            <com.google.android.material.textfield.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/email"
                android:inputType="textEmailAddress"
                android:text="@={viewModel.emailText}" />
        </com.google.android.material.textfield.TextInputLayout>

        <com.google.android.material.textfield.TextInputLayout
            android:id="@+id/passwordTextInputLayout"
            style="@style/myTextInputLayoutStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginTop="24dp"
            android:layout_marginEnd="16dp"
            app:errorText="@{viewModel.passwordErrorMessage}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/emailTextInputLayout">

            <com.google.android.material.textfield.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/password"
                android:inputType="textPassword"
                android:text="@={viewModel.passwordText}" />

        </com.google.android.material.textfield.TextInputLayout>

        <TextView
            android:id="@+id/createAccountTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="?attr/selectableItemBackground"
            android:clickable="true"
            android:text="@string/create_one"
            android:textColor="@color/textBlue"
            app:layout_constraintBottom_toBottomOf="@+id/noAccountTextview"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toEndOf="@+id/noAccountTextview"
            app:layout_constraintTop_toTopOf="@+id/noAccountTextview" />
<!-- **here is the important include**-->
        <include
            android:id="@+id/here_must_be_id_or_no_databinding"
            android:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}"
            layout="@layout/loading_state" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

i included the whole XML for clarification.
then in your view model add this

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

// Override ViewModelProvider.NewInstanceFactory to create the ViewModel (VM).
class SignInViewModelFactory(private val repository: NoteRepository) :
    ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T = SignInViewModel(repository) as T
}


class SignInViewModel(val repository: NoteRepository) : ViewModel() {


    private val _isLoading = MutableLiveData(true)

    val emailText = MutableLiveData("")
    val passwordText = MutableLiveData("")


    val isLoading: LiveData<Boolean> = _isLoading


    fun logIn() {
         //start loading, this will make the view start loading directly
        _isLoading.value = true
        if (isValidInput()) {
            val res = repository.logIn(LoginUser(emailText.value!!, passwordText.value!!))
        }//remove loading view
        _isLoading.value = false
    }

//code ..
}

notice that you are observing isLoading variable inside the XML so whenever its value is changed the view will observe the change and start act on it.


Well you can use liveData for this :D

At your ViewModel class you can create a live data object like this

 MutableLiveData<Boolean> isLoading = new MutableLiveData<>();

and for example make a function called downloadFinished and call it in the onComplete for your remote code

 private void downloadFinished() {
        isLoading.setValue(true);
    }

At your activity that use the view model you observe the value of the loading and hide the progress or what ever you want

   TestViewModel viewModel = ViewModelProviders.of(this).get(TestViewModel.class);
        viewModel.isLoading.observe(this, new Observer<Boolean>() {
            @Override
            public void onChanged(@Nullable Boolean isLoading) {
                if (isLoading != null) {
                    if (isLoading) {
                        // hide your progress bar
                    }
                }
            }
        });

Tags:

Android

Mvvm