在 Android Studio 中的双向数据绑定中更改 LiveData<MVoice> 时如何重置 LiveData<Boolean>?
How can I reset LiveData<Boolean> when LiveData<MVoice> is changed in Two way data binding in Android Studio?
EditText 使用双向数据绑定,Button 使用单向数据绑定 layout_detail.xml
我希望按钮在aDetailViewModel.aMVoice.name
改变时启用。
当EditText的内容改变时,DetailViewModel
中aMVoice
的值也会改变,我想我可以重新设置isChanged
的值,但我没有不知道怎么办?你能告诉我吗?
layout_detail.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">
<data>
<import type="android.view.View" />
<variable name="aDetailViewModel"
type="info.dodata.voicerecorder.viewcontrol.DetailViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/eTName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:text="@={aDetailViewModel.aMVoice.name}" />
<Button
android:id="@+id/btnSave"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="@{aDetailViewModel.isChanged}"
android:text="Save" />
</LinearLayout>
</layout>
代码
class DetailViewModel(private val mDBVoiceRepository: DBVoiceRepository, private val voiceId:Int) : ViewModel() {
val aMVoice=mDBVoiceRepository.getVoiceById(voiceId) //I hope to reset isChanged when aMVoice is changed
val isChanged: LiveData<Boolean> = MutableLiveData<Boolean>(false)
}
class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){
fun getVoiceById(id:Int)=mDBVoiceDao.getVoiceById(id)
}
@Dao
interface DBVoiceDao{
@Query("SELECT * FROM voice_table where id=:id")
fun getVoiceById(id:Int):LiveData<MVoice>
}
@Entity(tableName = "voice_table", indices = [Index("createdDate")])
data class MVoice(
@PrimaryKey (autoGenerate = true) @ColumnInfo(name = "id") var id: Int = 0,
var name: String = "",
var path: String = "",
)
试试看
class DetailViewModel(private val mDBVoiceRepository: DBVoiceRepository, private val voiceId: Int) :
ViewModel() {
val aMVoice =
mDBVoiceRepository.getVoiceById(voiceId) //I hope to reset isChanged when aMVoice is changed
val isChanged: MutableLiveData<Boolean> = MutableLiveData<Boolean>(false)
fun listenChanges(owner: LifecycleOwner) {
aMVoice.observe(owner) {
isChanged.value?.apply {
isChanged.value = !this
}
}
}
}
并从你所在的位置调用监听器 lifecycle owner - 例如从 Fragment
class Fragment1 : Fragment() {
lateinit var viewModel: DetailViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.listenChanges(viewLifecycleOwner)
}
}
由于您想找出多个属性的值变化,我们应该通过 map
转换在特定的 LiveData
中将每个属性分开。这些新的 LiveData
起到了 two-way 绑定的作用。
最后,要整合变化,最好的方法是使用一个MediatorLiveData
来触发每个变化,然后检查值。因此,检查来自 DB
的初始值与来自视图的接收值是否相等就足够了。
class DetailViewModel(...) {
private val aMVoice = mDBVoiceRepository.getVoiceById(voiceId)
val voiceName = aMVoice.map { it.name } as MutableLiveData<String>
val voicePath = aMVoice.map { it.path } as MutableLiveData<String>
// ... (similar for more attributes)
val isChanged = MediatorLiveData<Boolean>().apply {
addSource(voiceName) { postValue(currentMVoice != aMVoice.value) }
addSource(voicePath) { postValue(currentMVoice != aMVoice.value) }
// ... (similar for more attributes)
}
// it collects current values delivered from views as a `MVoice` object.
private val currentMVoice: MVoice?
get() = aMVoice.value?.copy(
name = voiceName.value ?: "",
path = voicePath.value ?: "",
// ... (similar for more attributes)
)
}
然后在布局中使用voiceName
、voicePath
、等:
<EditText
android:id="@+id/eTName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:text="@={aDetailViewModel.voiceName}" />
<EditText
android:id="@+id/eTPath"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="text"
android:text="@={aDetailViewModel.voicePath}" />
// ...
要使用 map
转换,请不要忘记在 build.gradle
中添加 lifecycle-livedata-ktx
依赖项(如果之前没有)。
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
EditText 使用双向数据绑定,Button 使用单向数据绑定 layout_detail.xml
我希望按钮在aDetailViewModel.aMVoice.name
改变时启用。
当EditText的内容改变时,DetailViewModel
中aMVoice
的值也会改变,我想我可以重新设置isChanged
的值,但我没有不知道怎么办?你能告诉我吗?
layout_detail.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">
<data>
<import type="android.view.View" />
<variable name="aDetailViewModel"
type="info.dodata.voicerecorder.viewcontrol.DetailViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/eTName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:text="@={aDetailViewModel.aMVoice.name}" />
<Button
android:id="@+id/btnSave"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="@{aDetailViewModel.isChanged}"
android:text="Save" />
</LinearLayout>
</layout>
代码
class DetailViewModel(private val mDBVoiceRepository: DBVoiceRepository, private val voiceId:Int) : ViewModel() {
val aMVoice=mDBVoiceRepository.getVoiceById(voiceId) //I hope to reset isChanged when aMVoice is changed
val isChanged: LiveData<Boolean> = MutableLiveData<Boolean>(false)
}
class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){
fun getVoiceById(id:Int)=mDBVoiceDao.getVoiceById(id)
}
@Dao
interface DBVoiceDao{
@Query("SELECT * FROM voice_table where id=:id")
fun getVoiceById(id:Int):LiveData<MVoice>
}
@Entity(tableName = "voice_table", indices = [Index("createdDate")])
data class MVoice(
@PrimaryKey (autoGenerate = true) @ColumnInfo(name = "id") var id: Int = 0,
var name: String = "",
var path: String = "",
)
试试看
class DetailViewModel(private val mDBVoiceRepository: DBVoiceRepository, private val voiceId: Int) :
ViewModel() {
val aMVoice =
mDBVoiceRepository.getVoiceById(voiceId) //I hope to reset isChanged when aMVoice is changed
val isChanged: MutableLiveData<Boolean> = MutableLiveData<Boolean>(false)
fun listenChanges(owner: LifecycleOwner) {
aMVoice.observe(owner) {
isChanged.value?.apply {
isChanged.value = !this
}
}
}
}
并从你所在的位置调用监听器 lifecycle owner - 例如从 Fragment
class Fragment1 : Fragment() {
lateinit var viewModel: DetailViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.listenChanges(viewLifecycleOwner)
}
}
由于您想找出多个属性的值变化,我们应该通过 map
转换在特定的 LiveData
中将每个属性分开。这些新的 LiveData
起到了 two-way 绑定的作用。
最后,要整合变化,最好的方法是使用一个MediatorLiveData
来触发每个变化,然后检查值。因此,检查来自 DB
的初始值与来自视图的接收值是否相等就足够了。
class DetailViewModel(...) {
private val aMVoice = mDBVoiceRepository.getVoiceById(voiceId)
val voiceName = aMVoice.map { it.name } as MutableLiveData<String>
val voicePath = aMVoice.map { it.path } as MutableLiveData<String>
// ... (similar for more attributes)
val isChanged = MediatorLiveData<Boolean>().apply {
addSource(voiceName) { postValue(currentMVoice != aMVoice.value) }
addSource(voicePath) { postValue(currentMVoice != aMVoice.value) }
// ... (similar for more attributes)
}
// it collects current values delivered from views as a `MVoice` object.
private val currentMVoice: MVoice?
get() = aMVoice.value?.copy(
name = voiceName.value ?: "",
path = voicePath.value ?: "",
// ... (similar for more attributes)
)
}
然后在布局中使用voiceName
、voicePath
、等:
<EditText
android:id="@+id/eTName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:text="@={aDetailViewModel.voiceName}" />
<EditText
android:id="@+id/eTPath"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="text"
android:text="@={aDetailViewModel.voicePath}" />
// ...
要使用 map
转换,请不要忘记在 build.gradle
中添加 lifecycle-livedata-ktx
依赖项(如果之前没有)。
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'