首页 > 解决方案 > 数据绑定不观察 ViewModel 中的 LiveData

问题描述

我正在尝试在 ViewModel 中将数据绑定与 LiveData 一起使用。但是当使用没有明确添加观察者的情况下不会触发的 Transformations.map 函数。XML 中的数据绑定不会为 ViewModel 中的 LiveData 生成观察者。

登录片段.kt

class LoginFragment : Fragment() {

    var homeViewModel: HomeViewModel? = null

    companion object {
        val TAG : String = "LoginFragment"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        homeViewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
    }


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val binding = DataBindingUtil.inflate<FragmentLoginBinding>(inflater, R.layout.fragment_login, container, false)
                .apply {
                    setLifecycleOwner(this@LoginFragment)
                    loginButton.setOnClickListener {
                        signInWithEmail()
                    }
        }

        return binding.root
    }

    /*private fun observeHomeFragmentUIDataLiveData() {
        homeViewModel?.homeFragmentUIDataLiveData?.observe(this, Observer {
            val email = it.email
            Toast.makeText(activity,email, Toast.LENGTH_SHORT).show()
        })
    }

    private fun observeLoginErrorEventLiveData() {
        homeViewModel?.loginErrorEventLiveData?.observe(this, Observer {
            Toast.makeText(activity,it, Toast.LENGTH_SHORT).show()
        })
    }*/

    /**
     * Sign In via Email
     */
    fun signInWithEmail(){
        val email = email_text_input_layout.editText?.text.toString()
        val password = password_text_input_layout.editText?.text.toString()

        var cancel : Boolean? = false
        var focusView : View? = null

        if(password.isEmpty()){
            password_text_input_layout.error = getString(R.string.this_field_is_required)
            focusView = password_text_input_layout
            cancel = true
        }

        if(email.isEmpty()){
            email_text_input_layout.error = getString(R.string.this_field_is_required)
            focusView = email_text_input_layout
            cancel = true
        }

        if(cancel!!){
            focusView?.requestFocus()
        }
        else{
            homeViewModel?.signInWithEmail(email,password)

            /*homeViewModel?.signInWithEmail(email,password)?.observe(this, Observer {
                val email = it
                Toast.makeText(activity,""+email, Toast.LENGTH_SHORT).show()
            })*/
        }
    }
}

片段登录.xml

<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>
        <variable name="homeViewModel" type="com.rramprasad.adminapp.HomeViewModel"/>
    </data>

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

<!-- Skipped some code for clarity -->

<androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/error_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{homeViewModel.loginErrorEventLiveData.value}"
        app:isGone="@{homeViewModel.isLoginSuccess}"
        android:textColor="@android:color/holo_orange_dark"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/password_text_input_layout"
        app:layout_constraintBottom_toBottomOf="@id/login_button"
        />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

HomeViewModel.kt

class HomeViewModel(application: Application) : AndroidViewModel(application) {

    val homeRepository: HomeRepository?

    init {
        homeRepository = HomeRepository()
    }

    // UI Data for HomeFragment
    val homeFragmentUIDataLiveData : MutableLiveData<HomeFragmentUIData> = MutableLiveData()

    // UI Data for LoginFragment
    val loginErrorEventLiveData : MutableLiveData<String> = MutableLiveData()

    var isLoginSuccess: LiveData<Boolean>? = null

    fun signInWithEmail(email: String, password: String) : LiveData<Boolean>? {
        val signInResponseMutableLiveData : MutableLiveData<Any> = homeRepository?.signInWithEmail(email, password)!!

        isLoginSuccess = Transformations.map(signInResponseMutableLiveData) { signInResponse ->
            when (signInResponse) {
                (signInResponse is FirebaseUser) -> {
                    val firebaseUserEmail = (signInResponse as FirebaseUser).email
                    homeFragmentUIDataLiveData.value = HomeFragmentUIData(firebaseUserEmail ?: "")
                    return@map true
                }
                else -> {
                    loginErrorEventLiveData.value = signInResponse.toString()
                    return@map false
                }
            }
        }

        return isLoginSuccess
    }
}

绑定适配器.kt

@BindingAdapter("isGone")
fun bindIsGone(view: View, isGone: Boolean) {
    view.visibility = if (isGone) {
        View.GONE
    } else {
        View.VISIBLE
    }
}

安卓工作室版本:

Android Studio 3.2 RC 3
Build #AI-181.5540.7.32.4987877, built on August 31, 2018
JRE: 1.8.0_152-release-1136-b06 x86_64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
macOS 10.13.4

构建.gradle:

android {
    compileSdkVersion 28
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    dataBinding {
        enabled = true
    }
}

标签: androidmvvmviewmodelandroid-databindingandroid-livedata

解决方案


推荐阅读