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>
Activity
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
}
}
ViewModel
class RapportiAltriCostiViewModel(private val repositoryDB: DbRepository) : ViewModel() {
var num1= MutableLiveData<Int>()
var num2= MutableLiveData<Double>()
}
Would anyone know how to solve?
Thank you for your patience and help!
UPDATE
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) {
}
})
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.
Thanks for the patience once again!