【问题标题】:The expression cannot be inverted, to be used in a two-way binding in EditText error表达式不能反转,用于 EditText 错误中的双向绑定
【发布时间】:2022-01-04 12:52:54
【问题描述】:

这是我第一次使用数据绑定,所以我很困惑。尝试使用 MVVM 架构在 EditText 中实现双向数据绑定,并在我的构建中出现此错误:

表达式 'viewmodelClientUrl.getValue()' 不能反转,因此不能用于双向绑定

详情:getValue方法没有取反,必须给方法加上@InverseMethod注解,指明在双向绑定表达式中使用时应该使用哪个方法

我不明白这是什么意思

有我的LoginViewModel:

class LoginViewModel(
private val repository: MainRepository): ViewModel() {

private var _clientUrl = MutableLiveData<String?>()
private var _username = MutableLiveData<String?>()
private var _password = MutableLiveData<String?>()
private val validationError = ValidationError()

val clientUrl: LiveData<String?>
    get() = _clientUrl
val username: LiveData<String?>
    get() = _username
val password: LiveData<String?>
    get() = _password

fun onClick(){
    val clientUrl = clientUrl.toString().trim()
    val username = username.toString().trim()
    val password = password.toString().trim()
    validateCredentials(clientUrl, username, password)
}

private fun validateCredentials(clientUrl: String, username: String, password: String): Boolean {

    if(!Patterns.WEB_URL.matcher(clientUrl).matches() || clientUrl.isEmpty()) {
        validationError.isUrlValid = false
        return false
    }
    if(username.isEmpty()) {
        validationError.isUsernameValid = false
        return false
    }
    if(password.isEmpty()) {
        validationError.isUsernameValid = false
        return false
    }
    return true
}

这是我的布局

<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.example.redmining.ui.login.LoginViewModel"/>
    <variable
        name="validationError"
        type="com.example.redmining.model.ValidationError"/>
</data>


<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".ui.login.LoginFragment"
    android:background="@color/white">

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/urlTextInputLayout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="24dp"
        android:layout_marginTop="170dp"
        android:layout_marginEnd="24dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/clientUrl"
            android:text="@={viewmodel.clientUrl}"
            android:fontFamily="@font/poppins_light"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/url_hint"
            android:inputType="textUri"
            android:selectAllOnFocus="true"
            />

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/urlTextInputLayout"
        app:layout_constraintStart_toStartOf="@id/urlTextInputLayout"
        android:text="Invalid URL"
        android:textColor="@android:color/holo_red_light"
        android:textSize="12sp"
        android:layout_marginTop="-8dp"
        android:layout_marginStart="5dp"
        android:visibility="@{validationError.urlValid ? View.GONE : View.VISIBLE}"/>


    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/usernameTextInputLayout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="24dp"
        android:layout_marginTop="4dp"
        android:layout_marginEnd="24dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/urlTextInputLayout">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fontFamily="@font/poppins_light"
            android:hint="@string/login"
            android:inputType="textEmailAddress"
            android:selectAllOnFocus="true"
            android:text="@={viewmodel.username}" />

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/usernameTextInputLayout"
        app:layout_constraintStart_toStartOf="@id/usernameTextInputLayout"
        android:text="Enter your username"
        android:textColor="@android:color/holo_red_light"
        android:textSize="12sp"
        android:layout_marginTop="-8dp"
        android:layout_marginStart="5dp"
        android:visibility="@{validationError.usernameValid ? View.GONE : View.VISIBLE}"/>


    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/passwordTextInputLayout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="24dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="24dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/usernameTextInputLayout"
        app:passwordToggleEnabled="true">

        <com.google.android.material.textfield.TextInputEditText
            android:text="@={viewmodel.password}"
            android:id="@+id/password"
            android:fontFamily="@font/poppins_light"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/password"
            android:imeActionLabel="@string/log_in"
            android:imeOptions="actionDone"
            android:inputType="textPassword"
            android:selectAllOnFocus="true" />

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/passwordTextInputLayout"
        app:layout_constraintStart_toStartOf="@id/passwordTextInputLayout"
        android:text="Enter your password"
        android:textColor="@android:color/holo_red_light"
        android:textSize="12sp"
        android:layout_marginTop="-8dp"
        android:layout_marginStart="5dp"
        android:visibility="@{validationError.passwordValid ? View.GONE : View.VISIBLE}"/>


    <Button
        android:onClick="@{() -> viewmodel.onClick()}"
        android:id="@+id/login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="48dp"
        android:layout_marginEnd="48dp"
        android:layout_marginTop="16dp"
        android:backgroundTint="@color/black"
        android:enabled="true"
        android:fontFamily="@font/poppins_light"
        android:paddingLeft="30dp"
        android:paddingTop="20dp"
        android:paddingRight="30dp"
        android:paddingBottom="20dp"
        android:text="@string/log_in"
        android:textAllCaps="true"
        android:textColor="@color/white"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/passwordTextInputLayout"/>

</androidx.constraintlayout.widget.ConstraintLayout>

【问题讨论】:

  • clientUrl 没有设置器。阅读this
  • 只需放松需要双向绑定的MutableLiveData&lt;T&gt; 对象的可见性修饰符并在xml 中引用它们,目前您正在绑定只读LiveData&lt;T&gt; 单向绑定对象。在大多数情况下,Android 已经有了反向绑定适配器来补充双向绑定的绑定适配器。
  • @Mark 非常感谢!真的很有帮助

标签: android kotlin mvvm data-binding viewmodel


【解决方案1】:

我认为你不需要 2 路绑定,因为没有它它也能正常工作。

    android:text="@{viewmodel.clientUrl}"

即使你需要使用它,也有两种方法:

第一种方法:

使用 MutableLiveData 代替 LiveData,因为 LiveData 不提供 setter。 将_clientUrl 设为非私有并且:

        android:text="@{viewmodel._clientUrl}"

第二种方法:

如果您想使用 LiveData,请使用afterTextChanged 设置值:

在 ViewModel 中:

fun updateClientUrl(s: Editable) {
    _clientUrl.value = s.toString();
}

在 XML 中:

        android:text="@{viewmodel.clientUrl}"
        android:afterTextChanged="@{viewmodel.updateClientUrl}"

所有解决方案都可以工作:

【讨论】:

  • 为什么是不必要的样板文件?这只是进行双向数据绑定的手动方式,因为 OP 以 MutableLiveData 开头,为什么当他们的 XML 文件正常时你会提倡这种方法?
  • 在你实现它之前你不会理解它。
  • 不,我完全理解你在这里所做的 - 手动双向绑定,因此我的评论。同样,更新数据(如编辑文本字段)是双向数据绑定的完美候选者,它使用@={mutableLiveData}@={mutableStateFlow} 语法提供开箱即用。不需要除此之外的任何东西。
  • 是的,但是大多数人不让 mutableLiveData 访问其他类,因此将它们设为私有。所以其他解决方案适合他们:)
猜你喜欢
  • 2020-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-23
  • 2015-03-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多