5
votes

Obtenez une valeur de LiveData

J'ai LiveData pour les livres dans le constructeur de ViewModel :

public void insertBook(Book book) {
    LiveData<Integer> bookMax = Transformations.map(books,  
        list -> {
          int value = 0;
          for(Book b: list) {
              if (value < b.getBookOrder()) {
                 value = b.getBookOrder();
              }
          }
        }
    );
    book.setBookOrder(bookMax + 1)
    bookRepository.update(book);
}

Lorsque l'utilisateur crée un nouveau livre code > depuis l'interface utilisateur, je veux que l'attribut book_order soit rempli avec un maximum incrémenté de book_order d'autres livres. Pour mieux décrire ce que je veux, voir le preudocode suivant:

book.book_order = max(books.book_order) + 1;

Donc, quand il y a trois livres avec book_order 1, 2, 3 respectivement, nouveau livre aurait cet attribut défini sur 4.

La question est, comment puis-je faire cela avec LiveData dans ViewModel ? J'ai essayé d'utiliser Transformations.map avec le nouveau LiveData , mais cette approche ne fonctionne pas du tout, bookMax semble être null .

LiveData<List<Book>> books;

public MyViewModel(@NonNull Application application) {
    super(application);
    books = bookRepository.getBooks();
}

Des idées pour définir le maximum incrémenté pour le nouveau livre? Cela peut être une autre approche que celle décrite ici. ViewModel a été créé pour séparer la logique d'application de l'interface utilisateur. Cependant, cela ne semble pas faire cela dans ce cas, car si je veux observer la valeur, je dois être dans Activity . De plus, je n'ai trouvé aucune alternative pour obtenir une valeur unique de DB. Toute aide appréciée.


1 commentaires

Bonjour, je me trompe peut-être totalement, mais il semble que vous puissiez utiliser le champ id de l'entité Book . Pour moi, la fonctionnalité de book_order peut être totalement remplacée par l'identifiant du modèle. Et lorsque vous travaillez avec LiveData , vous pouvez abonner votre activité au ViewModel qui a une référence à votre LiveData qui devrait automatiquement afficher tous les livres dans votre disposition principale du flux principal-détail. Faites-moi savoir si quelque chose comme ça pourrait fonctionner et je publierai une réponse avec du code


5 Réponses :


3
votes

Sachez que vos livres sont des liveata et peuvent donc changer leur valeur de temps en temps.
Où est votre bookMax est une valeur unique qui doit être calculée au moment de l'insertion.

Pour insérer, vous avez besoin de:

  • obtenir la liste actuelle des livres
  • puis calculez bookMax
  • puis insérez-le.
// get current value. may be null!
List<Book> bookList = books.getValue();
// so we better handle it early
if (bookList == null) {
    bookList = new ArrayList<>();
}

// calculate max order
int maxOrder = -1;
for (Book book : bookList) {
    if (maxOrder < book.order) {
        maxOrder = book.order;
    }
}

// create new book
Book newBook = new Book();
newBook.order = maxOrder + 1;

// add book to the list
bookList.add(newBook);

// do with new list whatever you want
// for example, you can update live data (if it is a MutableLiveData)
books.setValue(bookList);

EDIT Voici le code Java

val bookList: List<Book> = books.value   // current value. may be null!

val bookMax: Int = bookList.maxBy { it.order }.order   // find max order

// insert
val newBooks = arrayListOf(bookList)
newBooks.add(newBook)

books.value = newBooks  // update your livedata


0 commentaires

0
votes

L'utilisation de LiveData n'aidera pas dans votre scénario.

LiveData dans DAO est exécuté sur un thread différent et la nouvelle valeur est publiée dans le code d'observateur ou dans votre cas, le rappel Transformation.map (). Vous devez donc accéder à book.id dans Transformation.map ().

Cependant, si vous insérez book dans Transformation.map (), cela déclencherait une boucle infinie car à chaque entrée de table, la mise à jour Transformation.map () serait appelée comme LiveData> changerait.

Donc, pour votre cas:

  1. Exposez une méthode qui expose l'ID du dernier livre
  2. Insérez une nouvelle entrée.
  3. Ajoutez un LiveData> pour recevoir une mise à jour et l'afficher dans l'interface utilisateur.

0 commentaires

0
votes

Au lieu de prendre tous les livres pour trouver la commande maximale de livres , vous devriez créer une méthode dans votre référentiel qui vous fournira le nombre maximal de book_order à partir de db.

Quelque chose comme le pseudo code ci-dessous:

public int getMaxBookOrder() {
    List<Book> books = getBooks().getValue();
    // calculating max order using loop
    int maxOrder = -1;
    for (Book book : books) {
        if (maxOrder < book.order) {
            maxOrder = book.order;
        }
    }
    return maxOrder;
}

Maintenant, tout ce que vous avez à faire est d'insérer un nouveau livre, vous pouvez utiliser cette variable maxOrder à des fins incrémentielles .

Donc, votre méthode d'insertion sera comme:

public void insertBook(Book book) {
    List<Book> books = bookRepository.getBooks().getValue();
    // calculating max order using loop
    int maxOrder = -1;
    for (Book book : books) {
        if (maxOrder < book.order) {
            maxOrder = book.order;
        }
    }
    book.setBookOrder(maxOrder + 1)
    bookRepository.update(book);
}

Ici, en supposant que vous utilisez ROOM pour la base de données persistante strong >, c'est la requête qui peut vous aider à obtenir votre book_order maximum :

List<Book> books = bookRepository.getBooks().getValue(); // Assuming getBooks() returns LiveData

Si ROOM n'est pas votre cas, peut faire avec une autre approche:

Nous récupérons d'abord la liste en utilisant la méthode du référentiel, puis nous en trouvons le maximum comme ci-dessous pseudo:

SELECT MAX(book_order) FROM Book

Ensuite, trouvons le maximum à partir de celui-ci puis incrémentez-le de un:

public void insertBook(Book book) {
    int maxOrder = bookRepository.getMaxBookOrder();
    book.setBookOrder(maxOrder + 1)
    bookRepository.update(book);
}

Même vous pouvez déplacer ce code de recherche de maximum vers la méthode de dépôt comme mentionné précédemment comme:

int maxOrder = bookRepository.getMaxBookOrder();


0 commentaires

0
votes

Si vous voulez vraiment le faire avec un LiveData , vous pouvez en créer un personnalisé:

val data = bookRepository.getBooks() // Make getBooks() return BooksLiveData now
data.addBook(userCreatedBook) // This'll trigger observers as well

Ensuite, vous pouvez simplement le créer et utiliser: p >

class BooksLiveData(list: List<Book>) : LiveData<List<Book>>() {

    val initialList: MutableList<Book> = list.toMutableList()

    fun addBook(book: Book) {
        with(initialList) {
            // Assuming your list is ordered
            add(book.copy(last().bookOrder + 1))
        }

        postValue(initialList)
    }
}

Vous pouvez toujours observer ces données en direct, puisqu'elles publient initialList lorsqu'un livre est ajouté, elles avertiront les observateurs. Vous pouvez le modifier davantage, par exemple pour renvoyer le livre qui a été ajouté, etc.

Remarque: Il peut être préférable d'étendre à partir de MutableLiveData à la place , puisque LiveData n'est pas censé mettre à jour sa valeur, mais en interne, vous publiez quelque chose, donc cela peut prêter à confusion.


0 commentaires

0
votes

Approche 1: Vous pouvez utiliser un champ d'incrémentation automatique ou la PrimaryKey tel qu'un id pour la fonctionnalité de book_order . Vous pouvez même le nommer book_order si vous le souhaitez. Faites en sorte que votre classe de modèle ou d'entité ressemble à:

@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertBook(Book book);

Pour que le book_order soit incrémenté sur chaque livre ajouté à la base de données.

Ensuite, vous pouvez avoir vos classes ViewModel comme:

public void insertBook(final Book book) {
    executor.execute(new Runnable() {
        @Override
        public void run() {
            mDb.bookDao().insertBook(book);
        }
    });
}

Vous pouvez maintenant abonner votre liste d'activité à ce ViewModel (c.-à-d. faites en sorte que votre activité observe le ViewModel) en appelant la méthode suivante dans onCreate() de votre activité :

public void insertBook(Book book) {
    Book book= mLiveBook.getValue(); //declare mLiveBook as MutableLiveData<Book> in this ViewModel

//maybe put some validation here or some other logic

    mRepository.insertBook(book);
}

En faisant ces choses, vous serez capable de recevoir des mises à jour, ce est à dire. chaque fois qu'un Livre est ajouté à votre base de données par l'utilisateur, la vue Liste / Recycleur devrait afficher automatiquement le Book nouvellement ajouté .

Approche 2: Si ce n'est pas du tout ce que vous vouliez et que vous ne voulez trouver que la dernière commande du livre ajouté, vous pouvez ignorer complètement le troisième bloc de code et utiliser ce qui suit dans votre Dao :

XXX

qui donne le nombre total de livres ie. lignes dans le tableau des livres, que vous pouvez ensuite appeler à partir de votre référentiel, qui à son tour peut être appelé à partir de votre ViewModel qui à son tour peut être appelé à partir de l'activité.

Approche 3: strong > Si vous voulez le book_order qui, je pense, est le dernier nombre de livres dans la base de données après l'ajout d'un nouveau livre, vous pouvez utiliser l'approche 1 qui vous donne la liste des livres dans le ViewModel. Vous pouvez ensuite obtenir le nombre de livres à partir du nombre de livres.

Important! Dans tous les cas, vous voudriez modifier votre méthode insertBook () dans votre éditeur ou newBook ​​ViewModel et en faire quelque chose comme:

@Query("SELECT COUNT(*) FROM books")
int getCount();

et dans votre référentiel L'insert correspondant ressemblerait à:

private void initViewModel() {
    final Observer<List<Book>> bookObserver= new Observer<List<Book>>() {
        @Override
        public void onChanged(@Nullable List<Book> books) {
            bookList.clear();
            bookList.addAll(books);

            if (mAdapter == null) {
                mAdapter = new BooksAdapter(bookList, YourActivity.this);
                mRecyclerView.setAdapter(mAdapter);
            } else {
                mAdapter.notifyDataSetChanged();
            }
        }
    };

    mViewModel = ViewModelProviders.of(this)
            .get(MyViewModel.class);

    mViewModel.mBooks.observe(this, notesObserver); //mBooks is member variable in ViewModel class
}

et la méthode Dao correspondante:

public class MyViewModel extends AndroidViewModel {
    private BookRepository bookRepository;
    LiveData<List<Book>> books;

    public MyViewModel(@NonNull Application application) {
        super(application);
        bookRepository = AppRepository.getInstance(application.getApplicationContext());
        books = bookRepository.getBooks();
    }

}


0 commentaires