web-dev-qa-db-ja.com

複数のパラメーターを持つMediatorLiveDataまたはswitchMap変換

使っています - Transformations.switchMap ViewModelで、フラグメントで観察される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
    });
}

問題

ここで別のパラメーター(上記のコードでコメントされたnbDays)が必要なので、LiveDataオブジェクトは両方のパラメーターの変更(codenbDays)に反応します。

変換を連鎖するにはどうすればよいですか?

MediatorLiveData を示している読み物もありますが、それで問題は解決しません(2つのパラメーターで単一のDB関数を呼び出す必要があり、2つのliveDataをマージする必要はありません)。

そこで、switchMapの代わりにこれを試しましたが、codenbDaysは常にnullです。

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

1つの解決策は、オブジェクトを単一のパラメータとして渡すことです。これには簡単な解決策があると確信しています。

19
Yann39

ソース: 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)
}
28
jL4

@ 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
    });
}
7
Yann39

同様の問題に直面しました。これを解決するには2つの方法があります。

  1. MediatorLiveDataを使用します
  2. RxJavaには、このような種類の複雑な処理を行うためのさまざまな演算子があるため、使用します

RxJavaがわからない場合は、カスタムMediatorLiveDataクラスを記述することをお勧めします。カスタムMediatorLiveDataクラスの記述方法については、次の例をご覧ください。 https://Gist.github.com/AkshayChordiya/a79bfcc422fd27d52b15cdafc55eac6b

0
Akshay Chordiya