我有下一个用例:用户登录表格,输入姓名,电子邮件和密码,点击注册按钮。之后,系统需要检查是否已收到电子邮件,并根据该显示错误消息或创建新用户...
我正在尝试使用Room,ViewModel和LiveData。这是我尝试学习这些组件的一些项目,我没有远程api,我将所有内容存储在本地数据库中
所以我有这些课程:
所以我的想法是会有一个监听器附加到注册按钮,它会调用RegisterViewModel::register()
方法。
class RegisterViewModel extends ViewModel {
//...
public void register() {
validationErrorMessage.setValue(null);
if(!validateInput())
return;
registrationService.performRegistration(name.get(), email.get(), password.get());
}
//...
}
所以这是基本的想法,我也想让performRegistration
给我新建的用户。
最困扰我的是我不知道如何在服务中实现performRegistration
功能
class UsersRegistrationService {
private UsersRepository usersRepo;
//...
public LiveData<RegistrationResponse<Parent>> performRegistration(String name, String email, String password) {
// 1. check if email exists using repository
// 2. if user exists return RegistrationResponse.error("Email is taken")
// 3. if user does not exists create new user and return RegistrationResponse(newUser)
}
}
据我所知,UsersRepository
中的方法应返回LiveData,因为UsersDAO
正在返回LiveData
@Dao
abstract class UsersDAO {
@Query("SELECT * FROM users WHERE email = :email LIMIT 1")
abstract LiveData<User> getUserByEmail(String email);
}
class UsersRepository {
//...
public LiveData<User> findUserByEmail(String email) {
return this.usersDAO.getUserByEmail(email);
}
}
所以我的问题是如何实现performRegistration()
函数以及如何将值传递回视图模型,然后如何将活动从RegisterActivity更改为MainActivity ...
答案 0 :(得分:2)
我做了一个基于@guness答案的方法。我发现限制为两个LiveData
不好。如果要使用3怎么办?我们需要为每种情况创建不同的类。因此,我创建了一个处理无限数量LiveData
的类。
/**
* CombinedLiveData is a helper class to combine results from multiple LiveData sources.
* @param liveDatas Variable number of LiveData arguments.
* @param combine Function reference that will be used to combine all LiveData data results.
* @param R The type of data returned after combining all LiveData data.
* Usage:
* CombinedLiveData<SomeType>(
* getLiveData1(),
* getLiveData2(),
* ... ,
* getLiveDataN()
* ) { datas: List<Any?> ->
* // Use datas[0], datas[1], ..., datas[N] to return a SomeType value
* }
*/
class CombinedLiveData<R>(vararg liveDatas: LiveData<*>,
private val combine: (datas: List<Any?>) -> R) : MediatorLiveData<R>() {
private val datas: MutableList<Any?> = MutableList(liveDatas.size) { null }
init {
for(i in liveDatas.indices){
super.addSource(liveDatas[i]) {
datas[i] = it
value = combine(datas)
}
}
}
}
答案 1 :(得分:1)
如果您希望两个值都不为空
fun <T, V, R> LiveData<T>.combineWithNotNull(
liveData: LiveData<V>,
block: (T, V) -> R
): LiveData<R> {
val result = MediatorLiveData<R>()
result.addSource(this) {
this.value?.let { first ->
liveData.value?.let { second ->
result.value = block(first, second)
}
}
}
result.addSource(liveData) {
this.value?.let { first ->
liveData.value?.let { second ->
result.value = block(first, second)
}
}
}
return result
}
答案 2 :(得分:0)
JoseAlcérreca可能有best answer for this:
fun blogpostBoilerplateExample(newUser: String): LiveData<UserDataResult> {
val liveData1 = userOnlineDataSource.getOnlineTime(newUser)
val liveData2 = userCheckinsDataSource.getCheckins(newUser)
val result = MediatorLiveData<UserDataResult>()
result.addSource(liveData1) { value ->
result.value = combineLatestData(liveData1, liveData2)
}
result.addSource(liveData2) { value ->
result.value = combineLatestData(liveData1, liveData2)
}
return result
}
答案 3 :(得分:0)
您可以使用我的帮助方法:
val profile = MutableLiveData<ProfileData>()
val user = MutableLiveData<CurrentUser>()
val title = profile.combineWith(user) { profile, user ->
"${profile.job} ${user.name}"
}
fun <T, K, R> LiveData<T>.combineWith(
liveData: LiveData<K>,
block: (T?, K?) -> R
): LiveData<R> {
val result = MediatorLiveData<R>()
result.addSource(this) {
result.value = block.invoke(this.value, liveData.value)
}
result.addSource(liveData) {
result.value = block.invoke(this.value, liveData.value)
}
return result
}
答案 4 :(得分:0)
您可以定义一个方法,该方法将使用MediatorLiveData组合多个LiveData,然后将此组合结果显示为元组。
public class CombinedLiveData2<A, B> extends MediatorLiveData<Pair<A, B>> {
private A a;
private B b;
public CombinedLiveData2(LiveData<A> ld1, LiveData<B> ld2) {
setValue(Pair.create(a, b));
addSource(ld1, (a) -> {
if(a != null) {
this.a = a;
}
setValue(Pair.create(a, b));
});
addSource(ld2, (b) -> {
if(b != null) {
this.b = b;
}
setValue(Pair.create(a, b));
});
}
}
如果您需要更多值,则可以创建一个CombinedLiveData3<A,B,C>
并公开一个Triple<A,B,C>
来代替对,等等。
答案 5 :(得分:0)
LiveData liveData1 = ...;
LiveData liveData2 = ...;
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
答案 6 :(得分:0)
如果您想在构建时创建字段和设置(使用 also
):
val liveData1 = MutableLiveData(false)
val liveData2 = MutableLiveData(false)
// Return true if liveData1 && liveData2 are true
val liveDataCombined = MediatorLiveData<Boolean>().also {
// Initial value
it.value = false
// Observing changes
it.addSource(liveData1) { newValue ->
it.value = newValue && liveData2.value!!
}
it.addSource(selectedAddOn) { newValue ->
it.value = liveData1.value!! && newValue
}
}
答案 7 :(得分:0)
没有自定义类
MediatorLiveData<Pair<Foo?, Bar?>>().apply {
addSource(fooLiveData) { value = it to value?.second }
addSource(barLiveData) { value = value?.first to it }
}.observe(this) { pair ->
// TODO
}
答案 8 :(得分:0)
其中许多答案都有效,但也假定 LiveData 泛型类型不可为空。
但是如果一个或多个给定的输入类型是可空类型(假设泛型的默认 Kotlin 上限是 Any?
,它可以为空)怎么办?
结果是即使 LiveData 发射器会发出一个值 (null
),MediatorLiveData 也会忽略它,认为这是他自己的子实时数据值没有被设置。
相反,这个解决方案通过强制传递给中介的类型的上限不为空来处理它。懒惰但需要。
此外,在调用组合器函数后,此实现避免了相同的值,这可能是您需要的,也可能不是您需要的,因此请随意删除那里的相等检查。
fun <T1 : Any, T2 : Any, R> combineLatest(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
combiner: (T1, T2) -> R,
): LiveData<R> = MediatorLiveData<R>().apply {
var first: T1? = null
var second: T2? = null
fun updateValueIfNeeded() {
value = combiner(
first ?: return,
second ?: return,
)?.takeIf { it != value } ?: return
}
addSource(liveData1) {
first = it
updateValueIfNeeded()
}
addSource(liveData2) {
second = it
updateValueIfNeeded()
}
}