如何使用 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 的在线示例并不多。
为了未来的读者着想,我会留下所有这些信息。保重。
概览:
您好。在我的应用程序中,用户可以在 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 的在线示例并不多。
为了未来的读者着想,我会留下所有这些信息。保重。