具有多个参数的 MediatorLiveData 或 switchMap 转换

新手上路,请多包涵

我在我的 ViewModel 中使用 Transformations.switchMap 所以我的 LiveData 集合,在我的片段中观察到,对 code 参数的变化做出反应。

这非常有效:

 public class MyViewModel extends AndroidViewModel {

    private final LiveData<DayPrices> dayPrices;
    private final MutableLiveData<String> code = new MutableLiveData<>();
    // private final MutableLiveData<Integer> nbDays = new MutableLiveData<>();
    private final DBManager dbManager;

    public MyViewModel(Application application) {
        super(application);
        dbManager = new DBManager(application.getApplicationContext());
        dayPrices = Transformations.switchMap(
            code,
            value -> dbManager.getDayPriceData(value/*, nbDays*/)
        );
    }

    public LiveData<DayPrices> getDayPrices() {
        return dayPrices;
    }

    public void setCode(String code) {
        this.code.setValue(code);
    }

    /*public void setNbDays(int nbDays) {
        this.nbDays.setValue(nbDays);
    }*/

}

public class MyFragment extends Fragment {

    private MyViewModel myViewModel;

    myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
    myViewModel.setCode("SO");
    //myViewModel.setNbDays(30);
    myViewModel.getDayPrices().observe(MyFragment.this, dataList -> {
        // update UI with data from dataList
    });
}

问题

I now need another parameter ( nbDays commented in the code above), so that my LiveData object reacts on both parameters change ( code and nbDays )。

我怎样才能链式转换?

一些阅读将我指向 MediatorLiveData ,但它并没有解决我的问题(仍然需要使用 2 个参数调用单个 DB 函数,我不需要合并 2 个 liveData)。

所以我尝试了这个而不是 switchMap 但是 codenbDays 总是空的。

 dayPrices.addSource(
    dbManager.getDayPriceData(code.getValue(), nbDays.getValue),
    apiResponse -> dayPrices.setValue(apiResponse)
);

一种解决方案是将对象作为单个参数传递,我很确定有一个简单的解决方案。

原文由 Yann39 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 497
2 个回答

资料来源:https: //plus.google.com/+MichielPijnackerHordijk/posts/QGXF9gRomVi

要为 switchMap() 设置多个触发器,您需要使用自定义 MediatorLiveData 来观察 LiveData 对象的组合 -

 class CustomLiveData extends MediatorLiveData<Pair<String, Integer>> {
    public CustomLiveData(LiveData<String> code, LiveData<Integer> nbDays) {
        addSource(code, new Observer<String>() {
            public void onChanged(@Nullable String first) {
                setValue(Pair.create(first, nbDays.getValue()));
            }
        });
        addSource(nbDays, new Observer<Integer>() {
            public void onChanged(@Nullable Integer second) {
                setValue(Pair.create(code.getValue(), second));
            }
        });
    }
}

然后你可以这样做 -

 CustomLiveData trigger = new CustomLiveData(code, nbDays);
LiveData<DayPrices> dayPrices = Transformations.switchMap(trigger,
    value -> dbManager.getDayPriceData(value.first, value.second));

如果您使用 Kotlin 并希望使用泛型:

 class DoubleTrigger<A, B>(a: LiveData<A>, b: LiveData<B>) : MediatorLiveData<Pair<A?, B?>>() {
    init {
        addSource(a) { value = it to b.value }
        addSource(b) { value = a.value to it }
    }
}

然后:

 val dayPrices = Transformations.switchMap(DoubleTrigger(code, nbDays)) {
    dbManager.getDayPriceData(it.first, it.second)
}

原文由 jL4 发布,翻译遵循 CC BY-SA 4.0 许可协议

@jL4 提出的自定义 MediatorLiveData 效果很好,可能是解决方案。

我只是想分享我认为最简单的解决方案是使用内部类来表示组合的过滤器值:

 public class MyViewModel extends AndroidViewModel {

    private final LiveData<DayPrices> dayPrices;
    private final DBManager dbManager;
    private final MutableLiveData<DayPriceFilter> dayPriceFilter;

    public MyViewModel(Application application) {
        super(application);
        dbManager = new DBManager(application.getApplicationContext());
        dayPriceFilter = new MutableLiveData<>();
        dayPrices = Transformations.switchMap(dayPriceFilter, input -> dbManager.getDayPriceData(input.code, input.nbDays));
    }

    public LiveData<DayPrices> getDayPrices() {
        return dayPrices;
    }

    public void setDayPriceFilter(String code, int nbDays) {
        DayPriceFilter update = new DayPriceFilter(code, nbDays);
        if (Objects.equals(dayPriceFilter.getValue(), update)) {
            return;
        }
        dayPriceFilter.setValue(update);
    }

    static class DayPriceFilter {
        final String code;
        final int nbDays;

        DayPriceFilter(String code, int nbDays) {
            this.code = code == null ? null : code.trim();
            this.nbDays = nbDays;
        }
    }

}

然后在活动/片段中:

 public class MyFragment extends Fragment {

    private MyViewModel myViewModel;

    myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
    myViewModel.setDayPriceFilter("SO", 365);
    myViewModel.getDayPrices().observe(MyFragment.this, dataList -> {
        // update UI with data from dataList
    });
}

原文由 Yann39 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题