How can I change in ViewModel source of my LiveData from Room Dao
Asked Answered
N

4

6

How can I change in ViewModel source of my LiveData from Room Dao. In WordDao I have two queries:

@Dao
public interface WordDao {

   @Query("SELECT * FROM " + Word.TABLE_NAME + " ORDER BY text ASC")
   LiveData<List<Word>> getWordsByAsc();

   @Query("SELECT * FROM " + Word.TABLE_NAME + " ORDER BY text DESC")
   LiveData<List<Word>> getWordsByDesc();
}

I have also Repository class:

public class WordRepository {
   public LiveData<List<Word>> getWordsByAsc() {
       return wordDao.getWordsByAsc();
   }

   public LiveData<List<Word>> getWordsByDesc() {
       return wordDao.getWordsByDesc();
   }
}

and my ViewModel class:

public class WordViewModel extends AndroidViewModel {
    private boolean isSortAsc = true;
    private LiveData<Word> words;
    private WordRepository wordRepository;

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

        wordRepository = new WordRepository(application);
        words = wordRepository.getWordsByAsc();
    }

    public LiveData<List<Word>> getWords() {
       return words;
    }

    public void sortButtonClicked() {
       isSortAsc = !isSortAsc;
       //TODO how change here source of data depending on the isSortAsc
       //It works only after change configuration of screen, not insta
       words = isSortAsc ? wordRepository.getWordsByAsc() : 
            wordRepository.getWordsByDesc()
    }
}

In my Activity I have added observer:

 wordViewModel.getWords().observe(this, new Observer<List<Word>>() {
      @Override
      public void onChanged(@Nullable List<Word> words) {
          adapter.setWords(words);
      }
 });

In setWords(words) method I call also "notifyDataSetChanged()".

How change in the viewModel class source of LiveData depending on the "isSortAsc" parameter (words = isSortAsc ? wordRepository.getWordsByAsc() : wordRepository.getWordsByDesc()

It works only after change configuration of screen, not after insta change source for LiveData words

Neese answered 3/1, 2018 at 17:7 Comment(0)
F
3

One approach might be to use MediatorLiveData ....for example:

val words = MediatorLiveData<Word>().apply {
    this.addSource(sortDirection) {
        if (sortDirection.value) {
             this.value = wordRepository.getWordsByAsc()
        } else {
             this.value = wordRepository.getWordsByDesc()
        }
    }
}

I do something similar in following (with setting of direction) https://github.com/joreilly/galway-bus-android/blob/master/app/src/main/java/com/surrus/galwaybus/ui/viewmodel/BusStopsViewModel.kt

Fortalice answered 3/1, 2018 at 17:13 Comment(9)
I made in constructor sth like that ' words.setValue(wordRepository.getWordsByAsc().getValue()); words.addSource(isSortAsc, new Observer<Boolean>() { @Override public void onChanged(@Nullable Boolean isSort) { words.postValue(isSort ? wordRepository.getWordsByAsc().getValue() : wordRepository.getWordsByDesc().getValue()); } });' but now I don't have any valuesNeese
note that isSortAsc needs to change to a LiveData value (your setter in ViewModel class would set isSortAsc.value (check the sample code I linked to for full example)Catcall
If I good understand, I made sth like this: "private MutableLiveData<Boolean> isSortAsc;", I set the in constructor: "isSortAsc = new MutableLiveData<>(); isSortAsc.setValue(true);" and in method: "public void sortButtonClicked() { isSortAsc.setValue(!isSortAsc.getValue()); }" and it doesn't work.Neese
value is always nullNeese
what value are you seeing is null (and where)?Catcall
wordRepository.getWordsByAsc().getValue() and wordRepository.getWordsByDesc().getValue() hmmm... now I tested one scenario and when I never called observe on above LiveData objecrts, value is always null, but when I add and remove observer, a list is returned in value, so I guess, that LiveData required "observe" call to get query from databaseNeese
@Neese can you perhaps try and adapt the code I linked to above.....it's very similar scenario to what you have.....in it I'm switching direction value based on user selection and that triggers that MediatorLiveData updateCatcall
@Neese did you get any further with this?Catcall
I tried it, but unfortunately this didn't work for my scenario (it is possible that I could made sth wrong with it, but it seems to me, that I did it like in your example). Did you maybe try to use it with Room Database? Like I said in previous comment, when I didn't call "observe" on LiveData object which content comes from Room Database, when I try set the value, it is always null after call "getValue()". When I call "observe" on LiveData, the value returned is not null and is correct. I think that maybe "QUERY" from Room is not called without call observe on LiveData. I don't know why.Neese
D
1

A solution using Transformations.switchMap

First make isSortAsc LiveData

private MutableLiveData<Boolean> isSortAsc = new MutableLiveData<Boolean>(true);

Then use Transformations.switchMap on isSortAsc like so:

words = Transformations.switchMap(isSortAsc, 
    value -> value ? wordRepository.getWordsByAsc() : wordRepository.getWordsByDesc())
Doviedow answered 27/2, 2021 at 3:41 Comment(0)
W
0

I think here it is simple solution, always query the table ASC ie and reverse the list only in case user choose descending. Collection.Sort() is provided by Android so it should be perform better.

 public void sortButtonClicked() {
       isSortAsc = !isSortAsc;
       //TODO how change here source of data depending on the isSortAsc
       //It works only after change configuration of screen, not insta
 words = wordRepository.getWordsByAsc();
      if(!isSortAsc) {
         Collections.reverse(words);
       }
}

I hope this will help you.

Windswept answered 5/1, 2018 at 6:8 Comment(0)
W
-1

I believe the following function needs to be modified

wordViewModel.getWords().observe(this, new Observer<List<Word>>() {
      @Override
      public void onChanged(@Nullable List<Word> words) {
          adapter.setAdapter(words);
          notifyDataSetChanged();
      }
 });

One more thing the words variable won't have any value until onChanged is triggered. So you have to make sure getWords() should be called only after onChanged()

Windswept answered 3/1, 2018 at 17:20 Comment(6)
It's my naming fault, should be something like that: adapter.setWords(words); In my adapter in setWords I have notifyDataSetChanged() and it does't workNeese
So do u have code to explain where getWords() will be triggered?Windswept
Below code is set up in activity wordViewModel.getWords().observe(this, new Observer<List<Word>>() { @Override public void onChanged(@Nullable List<Word> words) { adapter.setWords(words); } }); and I want update adapter list, when source of LiveData in ViewModel will change setWords in separate adapter class looks like: public void setWords(List<Word> allWords) { this.allWords = allWords; notifyDataSetChanged(); }Neese
The issue is wordViewModel.getWords().observe is already observed for getWordsByAsc() as this is happen at constructor. And when you tap on sortButtonClicked there is no change will happen to the observed livedata, it still refers the old, until observe recreates. that is what happening when you change the configuration.Windswept
do you have an idea, how can I handle it to calling change on this LiveData when source will change in ViewModel? I made around solution, where in activity I observe MutableLiveData<Boolean> isSortAsc and in onChanged of this Boolean depending on the value I removeObserver from one LiveData<List<Word>> and add observer to second LiveData<List<Word>> and it works, but I don't like this solution, because I have two LiveData<List<Word>> references in ViewModelNeese
Transformations.map will be another approach. developer.android.com/reference/android/arch/lifecycle/… It's nice to implement Transformation.map.Windswept

© 2022 - 2024 — McMap. All rights reserved.