使用LiveData的SeekBar和EditText的进展

我有TextInput和SeekBar。

我需要通过文本输入值(数字)更改搜索程序的值,并通过搜索程序更改的值更改文本值。使用View-Model和Live数据绑定

所以,我在viewmodel类中有两个调解器实时数据

ViewModel:

val progress: MediatorLiveData<Int> by lazy { MediatorLiveData<Int>() }
val progressText: MediatorLiveData<String> by lazy { MediatorLiveData<String>() }

在初始化范围内相互观察。

init {
  progress.apply { addSource(progressText) { postvalue(it.toInt()) }}
  progressText.apply { addSource(progress) { postvalue(it.toString()) }}
}

xml和数据绑定类似于:

<AppCompatSeekBar android:id="@+id/seekbar"
            style="@style/SeekerItemStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:progress="@={viewModel.progress}" />

 <EditText android:id="@+id/input_progress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@={viewModel.progressText}" />

使用LiveData的SeekBar和EditText的进展

(考虑输入文字)

但是,在这里,我陷入了无限循环,因为一个调解人的更改会影响另一个调解人,而另一个调解人的更改又回到另一个。

这里是否有最佳实践和更巧妙的解决方案来解决此逻辑?谢谢。

lbsky20090504 回答:使用LiveData的SeekBar和EditText的进展

您可以使用Transformations.distinctUntilChanged(liveData)

创建一个新的LiveData对象,该对象在更改源LiveData值之前不会发出值。如果equals()得出false,则认为该值已更改。

因此,通过使用distinctUntilChanged,我们可以通过检查源实时数据的值是否已更改来停止无限循环。


如果您使用的是 livedata-ktx 库,则可以使用以下代码

init {
   progress.apply { addSource(progressText.distinctUntilChanged()) { postValue(it.toInt()) }}
   progressText.apply { addSource(progress.distinctUntilChanged()) { postValue(it.toString()) }}
}

如果您不使用 livedata-ktx 库,则可以使用以下代码

init {
   progress.apply { addSource(Transformations.distinctUntilChanged(progressText)) { postValue(it.toInt()) }}
   progressText.apply { addSource(Transformations.distinctUntilChanged(progress)) { postValue(it.toString()) }}
}
,
  1. 这里不需要MediatorLiveData-MutableLiveData就足够了,因为您只需要能够更改值即可。
  2. 对于LiveData只使用一个progress就足够了,它应该在内部保留一个Int值。

现在输入代码:

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

open class BasicViewModel: ViewModel() {
    val progress: MutableLiveData<Int> by lazy { MutableLiveData<Int>() }
}

您的布局如下所示(请注意,viewModel.progress是如何将String转换为EditText的{​​{1}})

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="BasicViewModel"/>
    </data>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <EditText android:id="@+id/input_progress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="numberDecimal"
            android:text="@={`` + viewModel.progress}" />

        <SeekBar
            android:id="@+id/seekbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:progress="@={viewModel.progress}" />

    </LinearLayout>
</layout>

最后,您的Activity可能如下:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: ActivityMainBinding = DataBindingUtil.setContentView(
                this,R.layout.activity_main)
        binding.setLifecycleOwner(this)

        val viewModel = ViewModelProviders.of(this).get(BasicViewModel::class.java)
        binding.viewModel = viewModel
    }
}

编辑:

如果EditText中的文本与SeekBar中的值不同,则方法将有所不同。但是,只要有一个LiveData<charSequence>对象就可以实现这种情况。因此,假设EditText应该显示一个像progress/100这样的浮点值。然后,它可能如下所示:

BasicViewModel.kt

package com.example.android.databinding.basicsample.data

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel

open class BasicViewModel: ViewModel() {
    val progressLiveData = MutableLiveData("0.0")

    fun updateEditText(percentage: Int) {
        progressLiveData.value = percentage.toFloat().div(100).toString()
    }

    fun onEditTextTyped(): LiveData<Int> {
        return Transformations.switchMap(progressLiveData,{
            val liveData = MutableLiveData<Int>()
            try {
                liveData.value = it.toString().toFloat().times(100f).toInt()
            } catch (e: NumberFormatException) {
                // reset the progress bar if the progress text is invalid
                liveData.value = 0
            }
            liveData
        })
    }
}

activity_main.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="com.example.android.databinding.basicsample.data.BasicViewModel"/>
    </data>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.appcompat.widget.AppCompatEditText
            android:id="@+id/input_progress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@={viewModel.progressLiveData}" />

        <SeekBar
            android:id="@+id/seekbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:progress="@{viewModel.onEditTextTyped}"
            android:onProgressChanged="@{(seekBar,progress,fromUser) -> viewModel.updateEditText(progress)}" />

    </LinearLayout>
</layout>
,

您可以分别为搜索者和文本制作2对MediatorLiveData-LiveData。例如,第一个MediatorLiveData观察到搜索栏的变化,并且当应用更改时,它会更改EditText的LiveData,反之亦然:您拥有MediatorLiveData,它在每次EditText中发生更改时都会更改SeekBar的LiveData。

本文链接:https://www.f2er.com/3095226.html

大家都在问