简体   繁体   English

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

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

Overview:概述:

Greetings.你好。 In my app the user can enter a number in an EditText and that number is then used as the maximum range for generating random numbers from 1 to (the number the user entered), which should then be displayed in a TextView.在我的应用程序中,用户可以在 EditText 中输入一个数字,然后该数字用作生成从 1 到(用户输入的数字)的随机数的最大范围,然后应显示在 TextView 中。

Problem:问题:

When the number is entered by the user and the button is pressed to generate the random number within the TextView, it doesn't work as intended.当用户输入数字并按下按钮以在 TextView 中生成随机数时,它不会按预期工作。

For instance:例如:

Initially, the value the LiveData holds for bonusNumber is 1 as it should be (based on the init block), but when the button is pressed to display a random number 0 is displayed after the button is pressed to generate the random number, instead of an actual random number between 1 and whatever the user entered in the EditText.最初,LiveData保持用于值bonusNumber1,因为它应(基于上init块),但是当按钮被按下显示按钮被按下,以生成随机数之后显示的随机数为0,而不是一个介于 1 和用户在 EditText 中输入的任何内容之间的实际随机数。

Code:代码:

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

Fragment:分段:

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 Views(The Relevant Ones): 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" />

Repository:存储库:

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

Solution:解决方案:

As suggested by a comment from @AppDev I could use two-way data-binding.正如@AppDev 的评论所建议的,我可以使用双向数据绑定。

My previous code:我之前的代码:

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

Should be changed to:应改为:

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

Then my ViewModel should be updated as follows:然后我的 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())
   }
}
     

Note:笔记:

As stated in the comments, two way data-binding isn't always safe because it exposes the actual MutableLiveData which has write capabilities, while LiveData is read-only and safer, but in my case it wasn't applicable because that data, as stated before is read only and the data entered by the user wouldn't change.正如评论中所述,双向数据绑定并不总是安全的,因为它公开了具有写入功能的实际 MutableLiveData,而 LiveData 是只读且更安全的,但在我的情况下它不适用,因为该数据,如前面所述是只读的,用户输入的数据不会改变。 Thanks to the comments I was aware of my mistake.感谢评论,我意识到了我的错误。

So it would be safer to just capture the value from the EditText using the binding object within the Fragment and then setting the TextView to the value.因此,使用 Fragment 中的绑定对象从 EditText 捕获值,然后将 TextView 设置为该值会更安全。 However for my solution above, I just went with the two way data-binding just to show it in effect since there aren't many examples online for Kotlin.然而,对于我上面的解决方案,我只是使用了双向数据绑定来显示它的效果,因为 Kotlin 的在线示例并不多。

Just thought for future readers, I would leave all of this information.只是为将来的读者考虑,我会留下所有这些信息。 Take care.小心。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM