【问题标题】:MVVM notify View about loading stateMVVM 通知 View 加载状态
【发布时间】:2017-08-09 14:15:05
【问题描述】:

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

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

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

【问题讨论】:

    标签: android mvvm


    【解决方案1】:

    您可以为此使用 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
                        }
                    }
                }
            });
    

    【讨论】:

    • 您应该在 MutableLiveData 中使用 postValue 而不是 setValue,否则您将收到“无法在后台线程上调用 setValue”异常 :)
    【解决方案2】:

    你也可以使用 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 变量,因此无论何时更改其值,视图都会观察到更改并开始对其进行操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多