Android Livedata 未更新 EditText [英] Android Livedata not updating EditText
问题描述
我在 3 个不同的 EditText 中使用 Android LiveData.我必须显示将前两个 EditText 的值乘以第三个 EditText 的结果.我利用了这个网站上给我的建议,实际上第三个值是用前两个相乘的结果更新的.问题是更新不会实时发生,而只会在我离开并重新进入活动时发生.我附上了 XML 文件、活动和视图模型.
I am using Android LiveData in 3 different EditText. I have to show the result of multiplying the values of the first two EditText into the third EditText. I took advantage of an advice given to me on this site, and actually the third value is updated with the result of the multiplication of the first two. The problem is that the update does not happen live, but only happens when I leave and re-enter the activity. I am attaching the XML file, the activity, and the viewmodel.
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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/num1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:backgroundTint="@color/white"
android:inputType="number" />
<EditText
android:id="@+id/num2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:backgroundTint="@color/white"
android:inputType="numberDecimal" />
<EditText
android:id="@+id/num3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:backgroundTint="@color/white"
android:inputType="numberDecimal" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
活动
class MainActivity: AppCompatActivity() {
private lateinit var binding: MainActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
binding = DataBindingUtil.setContentView(
this,
R.layout.main_activity
)
viewModel=
ViewModelProvider(this, factory)[MainActivityViewModel::class.java]
initView(binding)
}
private fun initView(
binding:
MainActivityBinding
) {
viewModel.num1.value = root?.num1?: 0
viewModel.num2.value = root?.num2?: 0.0
viewModel.num1.observe(lifecycleOwner, Observer { newNum1->
binding.num1.setText(
newNum1.toString()
)
})
viewModel.num2.observe(lifecycleOwner, Observer { newNum2->
binding.num2.setText(
newNum2.toString()
)
})
binding.num1.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
viewModel.num1.value =
binding.num1.text?.toString()?.toInt()
?: 0
}
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
binding.num2.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
viewModel.num2.value =
binding.num2.text?.toString()?.toDouble()
?: 0.0
}
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
fun <A, B> LiveData<A>.combineWith(b: LiveData<B>): LiveData<Pair<A?, B?>> =
MediatorLiveData<Pair<A?, B?>>().apply {
var lastA: A? = this@combineWith.value
var lastB: B? = b.value
addSource(this@combineWith) {
lastA = it
value = Pair(lastA, lastB)
}
addSource(b) {
lastB = it
value = Pair(lastA, lastB)
}
}
viewModel.num1.combineWith(viewModel.num2)
.observe(
this,
Observer { (first, second) ->
if (first != null && second != null) {
binding.num3.setText((first * second).toString())
}
}
)
}
binding.num1.isFocusableInTouchMode = true
binding.num2.isFocusableInTouchMode = true
binding.num3.isFocusableInTouchMode = true
}
}
视图模型
class RapportiAltriCostiViewModel(private val repositoryDB: DbRepository) : ViewModel() {
var num1= MutableLiveData<Int>()
var num2= MutableLiveData<Double>()
}
有人知道怎么解决吗?
感谢您的耐心和帮助!
更新
我尝试使用 TextWatcher,但它进入循环:
I tried with TextWatcher but it goes in loop:
binding.num1.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
viewModel.num1.value =
binding.num1.text?.toString()?.toInt()
?: 0
}
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
binding.num2.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
viewModel.num2.value =
binding.num2.text?.toString()?.toDouble()
?: 0.0
}
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
在分配值后,我无法删除 TextWatcher,正如我在网站上的另一个问题中所读到的,因为我需要他们始终倾听.
And I can't remove the TextWatcher after assigning the value, as I read on another question on the site, because I need them to always listen.
再次感谢您的耐心!
推荐答案
与此类似的事情.为了避免循环更新,您可以将 onFirstChanged/onSecondChanged 中的新值与 liveData 中的值进行比较,并以这种方式跳过 liveData.value = newValue.
Something similar to this. To avoid cyclic updates you may just compare new value inside onFirstChanged/onSecondChanged with value in your liveData and skip liveData.value = newValue in that way.
class MainActivity : AppCompatActivity() {
private lateinit var binding: MainActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
binding = DataBindingUtil.setContentView(
this,
R.layout.main_activity
)
viewModel =
ViewModelProvider(this, factory)[MainActivityViewModel::class.java]
initView(binding)
}
private fun initView(
binding:
MainActivityBinding
) {
binding.num1.listenChanges { viewModel.onFirstChanged(it) }
binding.num2.listenChanges { viewModel.onSecondChanged(it) }
viewModel.num1
.observe(
lifecycleOwner,
Observer { num1Value ->
binding.num1.setText(num1Value.toString())
}
)
viewModel.num2
.observe(
lifecycleOwner,
Observer { num2Value ->
binding.num2.setText(num2Value.toString())
}
)
viewModel.num3
.observe(
lifecycleOwner,
Observer { result ->
binding.num3.setText(result.toString())
}
)
}
binding.num1.isFocusableInTouchMode = true
binding.num2.isFocusableInTouchMode = true
binding.num3.isFocusableInTouchMode = true
}
private fun EditText.listenChanges(textChanged: (String) -> Unit) {
addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
textChanged(s.toString())
}
})
}
class RapportiAltriCostiViewModel(private val repositoryDB: DbRepository) : ViewModel() {
val num1 = MutableLiveData<Int>(0)
val num2 = MutableLiveData<Double>(0.0)
val num3: LiveData<Double>
get() = num1.combineWith(num2) { first, second ->
(first ?: 0) * (second ?: 0.0)
}
fun onFirstChanged(newValue: Int) {
if (num1.value != newValue) {
num1.value = newValue
}
}
fun onSecondChanged(newValue: Double) {
if (num2.value != newValue) {
num2.value = newValue
}
}
private companion object {
private fun <A, B, R> LiveData<A>.combineWith(b: LiveData<B>, combine: (A?, B?) -> R?): LiveData<R> =
MediatorLiveData<R>().apply {
var lastA: A? = this@combineWith.value
var lastB: B? = b.value
addSource(this@combineWith) {
lastA = it
value = combine.invoke(lastA, lastB)
}
addSource(b) {
lastB = it
value = combine.invoke(lastA, lastB)
}
}
}
}
这篇关于Android Livedata 未更新 EditText的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!