Ad

ViewModel Manipulation In MVVM Architecture

- 1 answer

My Problem is, that i need to preprocess data from the Room Database before showing it in my View.

Therefore here some Context of my Application:

Im programming a "record card" android application. Therefore i have a Room Database where all my record-cards are stored. The Information in the Entitiy is:

@Entitiy:
- ID
- Question
- Answer
- Topic
- Boxnumber (Box depends on how often i was right with my Answer)

Around the Entity i have the normal Room Setup, i found in several tutorials, with: Dao, Database, Repository. Furthermore i have a ViewModel connected to the Repository. And a View for showing the current Question connected to the ViewModel.

My Idea:

My Idea was that the ViewModel can hold LiveData of all needed Cards (with a specific Topic for example). I need some algorithm on that data which will select me my next Card i need for the View. This depends on: How much cards have different boxnumbers, which cards was the last 10 Questions, etc.

My Problem:

In my ViewModel i recieve LiveData from the repository. Every Tutorial shows only displaying the Room-Database Data with an View. But i need to do some preprocessing inside the ViewModel. Accessing the LiveData with .getValue() is not working and only returning null objects. Observing the Data in the ViewModel is also not working, because you need an Activity for this.

I don't know where i should place the algorithm working with the data from the database. I don't want to put it in the View because i want to hold the current algorithm parameters inside the ViewModel.

Some Code to understand my Program better:

@Dao

@Query("SELECT * FROM Karteikarte WHERE BoxnummerMixed = :Boxnummer")
LiveData<List<Karteikarte>> getKarteikartenInBoxMixed(int Boxnummer);

@Query("SELECT * FROM Karteikarte WHERE BoxnummerTopic = :Boxnummer AND Thema = :thema")
LiveData<List<Karteikarte>> getKarteikartenInBoxTopic(int Boxnummer, int thema);

Repository

public LiveData<List<Karteikarte>> getKarteikarteInBoxMixed(int boxnummer){
    return karteikarteDao.getKarteikartenInBoxMixed(boxnummer);
}

public LiveData<List<Karteikarte>> getKarteikarteInBoxTopic(Thema thema, int boxnummer){
    return karteikarteDao.getKarteikartenInBoxTopic(thema.ordinal(), boxnummer);
}

ViewModel

public LearningViewModel(Application application){
    super(application);
    repository = new KarteikarteRepository(application);
}

//This method will be called from the View at onCreate
public void initLearning(){
    allCards = repository.getKarteikarteInBoxMixed(1);
}

//This method will be called from the View
public Karteikarte nextCard() {
        // here will be a more complex algorithm
        Random random = new Random();
        List<Karteikarte> list = allCards.getValue();
        return list.get(random.nextInt(list.size()));
}
Ad

Answer

Here i have used Transformations.switchMap() and Transformations.map()

For detail understanding of Transformations and MediatorLiveData, you can watch this Video from Android Dev Summit '18 - Fun with LiveData

private MutableLiveData<Integer> boxNumberLiveData = new MutableLiveData<>();
private final LiveData<List<Karteikarte>> allCardsLiveData;

//Observe this from Fragment or Activity
public final LiveData<Karteikarte> karteikarteLiveData;

LearningViewModel(){

    // when ever you change box number below function is called and list of Karteikarte will be updated
    allCardsLiveData = Transformations.switchMap(boxNumberLiveData, new Function<Integer, LiveData<List<Karteikarte>>>() {
        @Override
        public LiveData<List<Karteikarte>> apply(Integer number) {
            if(number == null)return null;
            return repository.getKarteikarteInBoxMixed(1);
        }
    });

    // when ever list of Karteikarte is changed, below function will be called and a random Karteikarte will be selected
    karteikarteLiveData = Transformations.map(allCardsLiveData, new Function<List<Karteikarte>, Karteikarte>() {
        @Override
        public Karteikarte apply(List<Karteikarte> list) {
            return getRandomKarteikarte(list);
        }
    });

}

public void initLearning(){
    //Enter box number here
    // you can modify box number anytime you want, everything will change respectively
    boxNumberLiveData.setValue(1);
}


private Karteikarte getRandomKarteikarte(@Nullable List<Karteikarte> list){
    if(list == null)return null;
    Random random = new Random();
    return list.get(random.nextInt(list.size()));
}

If you want to get the next random Karteikarte. The best way to implement this is to have a extra field in database.

@Entitiy:
- ID
- Question
- Answer
- Topic
- Boxnumber (Box depends on how often i was right with my Answer)
- isShown(boolean)

while querying the data you need to add extra condition that isShown is false so when you want to load next Karteikarte, you just need to update the current Karteikarte's isShown field to true

if you want to re-use already shown Karteikarte, you can just reset isShown values to false when you want start the process

Ad
source: stackoverflow.com
Ad