MVVM notify有关加载状态的视图

时间:2017-08-09 14:15:05

标签: android mvvm

我现在正在使用Google的LiveData,他们建议使用MVVM模式设计。对于我的一些请求,我使用RxJava2,并在SubscribeWith(...)中监听响应。

例如,当我按下一个按钮将一些数据发送到远程数据源时,我正在显示一些加载动画并希望将其隐藏在onComplete()事件中(在subscribeWith(...)中) )。问题是我无法访问ModelView中的View。怎么可能让View知道加载动画应该被隐藏?

我目前的想法是在ViewModel内部的界面中创建并在View中实现它。但它破坏了View和ViewModel分离的概念。

2 个答案:

答案 0 :(得分:17)

你可以使用liveData:D

在ViewModel类中,您可以像这样创建一个实时数据对象

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

例如创建一个名为downloadFinished的函数并在onComplete中调用它 用于远程代码

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

在使用视图模型的活动中,您会观察加​​载的值并隐藏进度或您想要的内容

   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
                    }
                }
            }
        });

答案 1 :(得分:0)

您也可以使用DataBinding 进行单独的布局以在任何地方重复使用 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> 

然后将其包含在所需的布局中

<?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>

我提供了整个XML进行说明。
然后在您的视图模型中添加

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 ..
}

请注意,您正在观察XML中的isLoading变量,因此无论何时更改其值,视图都会观察到该变化并开始对其进行操作。