Android Livedata 未更新 EditText [英] Android Livedata not updating EditText

查看:68
本文介绍了Android Livedata 未更新 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆