2

我有一种情况,我有一个页面的视图模型,该页面基于一些输入数据(id)呈现,其中的字段是从我的存储库初始化但可以由用户编辑。

我的存储库功能非常简单:

class AccountsRepository {
  public LiveData<Account> getAccount(long id) {
    return roomDb.accountDao().getAccount(id);
  }
}

我希望我的视图模型可以重新用于不同的帐户,所以我有一个LiveData<Long>将使用帐户 ID 进行设置的模型。

class EditAccountViewModel {

  private MutableLiveData<Long> accountId = new MutableLiveData<>();

  public void setAccountId(long id) {
    accountId.setValue(id);
  }
}

在我的视图模型中,我想公开一个name绑定到EditText视图的可变字段。该字段应由存储库中的数据初始化。如果我使用简单的不可编辑绑定,我可以让单向数据绑定工作:

class EditAccountViewModel {

  private MutableLiveData<Long> accountId = new MutableLiveData<>();
  public LiveData<String> name;

  EditAccountViewModel() {
    this.name = Transformations.map(
        Transformations.switchMap(accountId, repo::getAccount),
        account -> account.name);
  }
}

但是,我无法使用它绑定@={viewModel.name}它,因为它抱怨它不知道如何设置值。我尝试编写一个辅助类,这样使用底层MediatorLiveData来设置值,但看起来我的 onChanged 回调从未被调用:

class MutableLiveDataWithInitialValue<T> extends MutableLiveData<T> {
  MutableLiveDataWithInitialValue(LiveData<T> initialValue) {
    MediatorLiveData<T> mediator = new MediatorLiveData<>();
    mediator.addSource(
        initialValue,
        data -> {
          // This never gets called per the debugger.
          mediator.removeSource(initialValue);
          setValue(data);
        });
  }
}

我更新了视图模型如下:

class EditAccountViewModel {

  private MutableLiveData<Long> accountId = new MutableLiveData<>();
  public MutableLiveData<String> name;

  EditAccountViewModel() {
    this.name = new MutableLiveDataWithInitialValue<>(
        Transformations.map(
            Transformations.switchMap(accountId, repo::getAccount),
            account -> account.name));
  }
}

但是,当我这样做时,我的EditText字段永远不会设置数据库中的值,这是有道理的,因为onChanged回调永远不会在我MediatorLiveData的 in 中调用MutableLiveDataWithInitialValue

这似乎是一个很常见的用例,所以我想知道我在搞砸什么?

4

2 回答 2

0

我能够以一种非常不优雅的方式解决这个问题:

class EditAccountViewModel {
  private final Executor ioExecutor;

  public final MutableLiveData<String> name = new MutableLiveData<>();
  public final MutableLiveData<String> someOtherField = new MutableLiveData<>();

  public void setAccountId(long id) {
    ioExecutor.execute(() -> {
      Account account = repo.getAccountSync(id);
      name.postValue(account.name);
      someOtherField.postValue(account.someOtherField);
    });
  }

}

这很好,因为只有用户可以在初始化后编辑字段。但是有明显的竞争条件......例如,如果数据库花费太长时间进行评分并且用户在此之前开始输入值。

于 2019-08-29T07:49:13.960 回答
0

你的助手类是一个好的开始。似乎您忘记了 LiveData 对象(包括您的 MediatorLiveData)除非您观察它们,否则不会被触发 - 这就是您的中介的 onChange() 方法从未被调用的原因。如果您将观察/不观察动作传递给您的中介,它会按预期工作。

由于您将 ViewModel 绑定到视图,因此在构造函数中至少提供一些初始值可能是个好主意,就像您在常规 MutableLiveData 上所做的那样(即使该值很快被来自 LiveData 的值替换)。

爪哇:

public class MutableLiveDataWithInitialValue<T> extends MutableLiveData<T> {

    private MediatorLiveData<T> mediator = new MediatorLiveData<>();

    public MutableLiveDataWithInitialValue(T initialValue, LiveData<T> delayedInitialValue) {
        super(initialValue);
        mediator.addSource(
                delayedInitialValue,
                data -> {
                    mediator.removeSource(delayedInitialValue);
                    setValue(data);
                });
    }

    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        mediator.observe(owner, observer);
        super.observe(owner, observer);
    }

    @Override
    public void observeForever(@NonNull Observer<? super T> observer) {
        mediator.observeForever(observer);
        super.observeForever(observer);
    }

    @Override
    public void removeObserver(@NonNull Observer<? super T> observer) {
        mediator.removeObserver(observer);
        super.removeObserver(observer);
    }

    @Override
    public void removeObservers(@NonNull LifecycleOwner owner) {
        mediator.removeObservers(owner);
        super.removeObservers(owner);
    }
}

科特林:

class MutableLiveDataWithInitialValue<T>(initialValue: T, delayedInitialValue: LiveData<T>) : MutableLiveData<T>(initialValue) {

    private val mediator = MediatorLiveData<T>()

    init {
        mediator.addSource(delayedInitialValue) {
            mediator.removeSource(delayedInitialValue)
            value = it
        }
    }

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        mediator.observe(owner, observer)
        super.observe(owner, observer)
    }

    override fun observeForever(observer: Observer<in T>) {
        mediator.observeForever(observer)
        super.observeForever(observer)
    }

    override fun removeObserver(observer: Observer<in T>) {
        mediator.removeObserver(observer)
        super.removeObserver(observer)
    }

    override fun removeObservers(owner: LifecycleOwner) {
        mediator.removeObservers(owner)
        super.removeObservers(owner)
    }
}
于 2021-02-17T08:28:28.510 回答