带过滤器的 LiveData?

una*_*tan 7 android viewmodel android-livedata mediatorlivedata

我是 LiveData 的新手,最近一直在做一些测试。我有一个应用程序,我需要在其中显示可以过滤的数据(名称、类别、日期...)。过滤器也可以组合(名称+日期)。该数据来自使用 Retrofit + RXJava 进行的 API 调用。

我知道我可以直接在视图中获取数据,而无需使用 LiveData。但是,我认为使用 ViewModel + LiveData 会很有趣。首先,测试它的工作原理,同时避免在视图不活动时尝试设置数据(感谢 LiveData),并在配置更改时保存数据(感谢 ViewModel)。这些都是我以前必须手动处理的事情。

所以问题是我没有找到一种方法来轻松地使用 LiveData 处理过滤器。如果用户选择一个过滤器,我设法使其与 switchMap 一起使用:

return Transformations.switchMap(filter,
    filter -> LiveDataReactiveStreams.fromPublisher(
    repository.getData(filter).toFlowable(BackpressureStrategy.BUFFER)));
Run Code Online (Sandbox Code Playgroud)

如果他选择两个过滤器,我发现我可以使用自定义 MediatorLiveData,这就是我所做的。然而,这里的问题是我的存储库调用的次数与我拥有的过滤器的数量一样多,并且我无法同时设置两个过滤器

我的自定义 MediatorLiveData:

class CustomLiveData extends MediatorLiveData<Filter> {

    CustomLiveData(LiveData<String> name, LiveData<String> category) {
        addSource(name, name -> {
            setValue(new Filter(name, category.getValue()));
        });

        addSource(category, category -> {
            setValue(new Filter(name.getValue(), newCategory));
        });
    }
}
Run Code Online (Sandbox Code Playgroud)
CustomLiveData trigger = new CustomLiveData(name, category);

  return Transformations.switchMap(trigger,
     filter -> LiveDataReactiveStreams.fromPublisher(
        repository.getData(filter.getName(), filter.getCategory())
        .toFlowable(BackpressureStrategy.BUFFER)));
Run Code Online (Sandbox Code Playgroud)

我是否充分理解 MediatorLiveData 的用法?是否可以使用 LiveData 实现我想要实现的目标?

谢谢!

Ric*_*rdo 6

按照您在答案中设置的方式,每次您致电时updateData()都会向您的MediatorLiveData. 每次新的namecategory更改时是否都需要不同的源?

另外,您可以让您的存储库方法直接getData公开LiveData吗?最后,是否可以使用 Kotlin 代替 Java?

如果是这样,您可以通过有条件地检查onChanged每个源何时在 MediatorLiveData 中设置值来简化流程data

例如,按照此处的文档:

//Create a new instance of MediatorLiveData if you don't need to maintain the old sources
val data = MediatorLiveData<List<Data>>().apply {
    addSource(repository.getData(name, category)) { value ->
        if (yourFilterHere) {
            this.value = value
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

您还可以扩展这个想法,使用 Kotlin 创建扩展函数:

inline fun <T> LiveData<T>.filter(crossinline filter: (T?) -> Boolean): LiveData<T> {
        return MediatorLiveData<T>().apply {
            addSource(this@filter) {
                if (filter(it)) {
                    this.value = it
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后,在你的updateData()方法中你可以这样做:

val data = repository.getData(name, category)
    .filter { value ->
        // Your boolean expression to validate the values to return
    }
Run Code Online (Sandbox Code Playgroud)


una*_*tan 0

我认为我从错误的角度看待问题。如果我错了,请纠正我。

当我可以直接更新我的 LiveData 时,我试图根据其他 LiveData 的更改来更新 LiveData。我找到的解决方案是有一个由视图直接更新的 MediatorLiveData。它可能是 MutableLiveData,但由于我使用的是 LiveDataReactiveStreams 并且它不接受 MutableLiveData,所以我没有找到其他解决方案。

public class MainViewModel extends AndroidViewModel {

    // Had to use a MediatorLiveData because the LiveDataReactiveStreams does not accept a MutableLiveData
    private MediatorLiveData<List<Data>> data = new MediatorLiveData<>();

    public MainViewModel(@NonNull Application application) {
        super(application);

        data.addSource(
            LiveDataReactiveStreams.fromPublisher(
                repository.getData(name, category)
                    .toFlowable(BackpressureStrategy.BUFFER)
            ), value -> data.setValue(value));
    }

    public LiveData<List<Data>> getData() {
        return data;
    }

    public void updateData(String name, String category) {
        data.addSource(
            LiveDataReactiveStreams.fromPublisher(
                repository.getData(name, category)
                    .toFlowable(BackpressureStrategy.BUFFER)
            ), value -> data.setValue(value));
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在活动中我简单地这样称呼它:

viewModel.updateData(name, category);

我不知道在尝试添加另一个源之前是否应该删除一个源,但我的 API 调用仅使用此解决方案完成一次,并且我可以同时拥有两个过滤器。