如何使用 Kotlin 从 EditText 获取 LiveData 值并将其显示在 TextView 中? MVVM

How To Get LiveData Value From EditText and Display It In A TextView Using Kotlin? MVVM

概览:

您好。在我的应用程序中,用户可以在 EditText 中输入一个数字,然后将该数字用作生成从 1 到(用户输入的数字)的随机数的最大范围,然后应将其显示在 TextView 中。

问题:

当用户输入数字并按下按钮以在 TextView 中生成随机数时,它没有按预期工作。

例如:

最初,bonusNumber 的 LiveData 值应该是 1(基于 init 块),但是当按钮按下以显示随机数 0 在按下按钮以生成随机数后显示,而不是 1 和用户在 EditText 中输入的任何值之间的实际随机数。

代码:

ViewModel:

class QuickPickViewModel : ViewModel() {

    private val repository = Repository()

    // LiveData for the number entered by the user
    private var _userBonusNumber = MutableLiveData<Int>()
    val userBonusNumber: LiveData<Int>
        get() = _userBonusNumber

    // LiveData for the actual result of the randomly generated bonus number
    private var _bonusNumber = MutableLiveData<Int>()
    val bonusNumber: LiveData<Int>
        get() = _bonusNumber

    init {
        _userBonusNumber.value = 1
        _bonusNumber.value = 1
    }

    fun getRandomBonusNumber() {
        // Set the MutableLiveData to be displayed in the TextView, to the number the user entered
        _bonusNumber.value = repository.generateBonusRandomNumber(_userBonusNumber.value!!.toInt())
    }
}

片段:

class QuickPickFragment : Fragment() {

    private lateinit var binding: FragmentQuickPickBinding
    private lateinit var viewModel: QuickPickViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment using data binding
        binding = DataBindingUtil.inflate(
            inflater,
            R.layout.fragment_quick_pick,
            container,
            false
        )

        viewModel = ViewModelProvider(this).get(QuickPickViewModel::class.java)
        
        binding.quickPickViewModel = viewModel
        binding.lifecycleOwner = this

        return binding.root
    }
}

XML 观看次数(相关的):

 <TextView
                    android:id="@+id/bonus_result_text_view"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="16dp"
                    android:text="@{String.valueOf(quickPickViewModel.bonusNumber)}"
                    android:textAllCaps="true"
                    android:textStyle="bold"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintHorizontal_bias="0.498"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/main_result_text_view" />

    <EditText
                    android:id="@+id/bonus_set_edit_text"
                    android:layout_width="120dp"
                    android:layout_height="wrap_content"
                    android:layout_margin="16dp"
                    android:layout_marginStart="8dp"
                    android:layout_marginEnd="16dp"
                    android:ems="10"
                    android:hint="@string/enter_number_quick_pick_bonus_edit_text_hint"
                    android:importantForAutofill="no"
                    android:inputType="number"
                    android:text="@{String.valueOf(quickPickViewModel.userBonusNumber)}"
                    app:layout_constraintBaseline_toBaselineOf="@+id/bonus_set_title_text_view"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toEndOf="@+id/bonus_set_title_text_view" />

     <Button
                    android:id="@+id/button"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="16dp"
                    android:layout_marginEnd="16dp"
                    android:layout_marginBottom="16dp"
                    android:onClick="@{() -> quickPickViewModel.getRandomBonusNumber()}"
                    android:text="Generate"
                    android:textAllCaps="true"
                    android:textStyle="bold"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent" />

存储库:

class Repository {
    /**
     * Generate random number for the bonus number within a particular range set by user
     *
     * [maxNum]: the number that the user entered which acts as the max range for the random numbers
     */
    fun generateBonusRandomNumber(maxNum: Int): Int {
        val minNum = 1
        // Adding 1[minNum] to the bound makes sure the number the person entered is inclusive
        return Random().nextInt(((maxNum - minNum)) + minNum)
    }
}

解决方案:

正如@AppDev 的评论所建议的,我可以使用 two-way data-binding.

我之前的代码:

android:inputType="number"
android:text="@{String.valueOf(quickPickViewModel.userBonusNumber)}"

应改为:

android:inputType="number"
android:text="@={quickPickViewModel.userBonusNumber}"

然后我的 ViewModel 应该更新如下:

class QuickPickViewModel : ViewModel() {
            
  private val repository = Repository()
                     
  val userBonusNumber = MutableLiveData<String>()
                    
  private var _bonusNumber = MutableLiveData<Int>()
  val bonusNumber: LiveData<Int>
    get() = _bonusNumber
            
  init {
    userBonusNumber.value = "1"
    _bonusNumber.value = 1
       }
            
  fun getRandomBonusNumber() {
      _bonusNumber.value = 
      repository.generateBonusRandomNumber(userBonusNumber.value!!.toInt())
   }
}
     

注:

如评论中所述,两种方式 data-binding 并不总是安全的,因为它公开了具有写入功能的实际 MutableLiveData,而 LiveData 是 read-only 并且更安全,但就我而言不适用,因为如前所述,该数据是只读的,用户输入的数据不会更改。多亏了评论,我才意识到自己的错误。

因此,仅使用 Fragment 中的绑定对象从 EditText 捕获值,然后将 TextView 设置为该值会更安全。然而,对于我上面的解决方案,我只是采用了两种方式 data-binding 只是为了展示它的效果,因为 Kotlin 的在线示例并不多。

为了未来的读者着想,我会留下所有这些信息。保重。