J'utilise Transformations.switchMap
dans mon ViewModel pour que ma collection LiveData
, observée dans mon fragment, réagisse aux changements du paramètre code
.
Cela fonctionne parfaitement:
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
});
}
Problème
J'ai maintenant besoin d'un autre paramètre (nbDays
commenté dans le code ci-dessus), afin que mon objet LiveData
réagisse au changement des deux paramètres (code
et nbDays
).
Comment chaîner des transformations?
Certaines lectures m'ont montré MediatorLiveData , mais cela ne résout pas mon problème (j'ai toujours besoin d'appeler une seule fonction DB avec 2 paramètres, je n'ai pas besoin de fusionner 2 liveDatas).
J'ai donc essayé ceci au lieu de switchMap
mais code
et nbDays
sont toujours nuls.
dayPrices.addSource(
dbManager.getDayPriceData(code.getValue(), nbDays.getValue),
apiResponse -> dayPrices.setValue(apiResponse)
);
Une solution serait de passer un objet en paramètre unique par je suis presque sûr qu'il existe une solution simple à cela.
Source: https://plus.google.com/+MichielPijnackerHordijk/posts/QGXF9gRomVi
Pour avoir plusieurs déclencheurs pour switchMap()
, vous devez utiliser un MediatorLiveData
personnalisé pour observer la combinaison des objets 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));
}
});
}
}
Ensuite, vous pouvez le faire -
CustomLiveData trigger = new CustomLiveData(code, nbDays);
LiveData<DayPrices> dayPrices = Transformations.switchMap(trigger,
value -> dbManager.getDayPriceData(value.first, value.second));
Si vous utilisez Kotlin et souhaitez travailler avec des génériques:
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 }
}
}
Ensuite:
val dayPrices = Transformations.switchMap(DoubleTrigger(code, nbDays)) {
dbManager.getDayPriceData(it.first, it.second)
}
MediatorLiveData
personnalisé comme proposé par @ jL4 fonctionne très bien et est probablement la solution.
Je voulais juste partager la solution la plus simple qui, selon moi, consiste à utiliser une classe interne pour représenter les valeurs de filtre composées:
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;
}
}
}
Puis dans l'activité/le fragment:
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
});
}
J'ai fait face à un problème similaire. Il existe 2 façons de résoudre ce problème:
MediatorLiveData
Si vous ne connaissez pas RxJava, je vous recommande d'écrire votre classe MediatorLiveData
personnalisée. Pour savoir comment écrire une classe MediatorLiveData
personnalisée, consultez cet exemple: https://Gist.github.com/AkshayChordiya/a79bfcc422fd27d52b15cdafc55eac6b