Получить одно значение из LiveData - PullRequest
2 голосов
/ 13 апреля 2019

У меня есть LiveData для книг в конструкторе ViewModel:

LiveData<List<Book>> books;

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

Когда пользователь создает новый book из пользовательского интерфейса, я хочу, чтобы атрибут book_order был заполнен с увеличенным максимумомbook_order других книг.Чтобы лучше описать, что я хочу, см. Следующий преудокод:

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

Поэтому, когда есть три книги с book_order 1, 2, 3 соответственно, новая книга будет иметь этот атрибут равным 4.

Вопрос в том, как я могу сделать это с LiveData в ViewModel?Я пытался использовать Transformations.map для нового LiveData, но этот подход вообще не работает, bookMax кажется null.

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

Любые идеи, как установить увеличенный максимум доновая книга?Это может быть другой подход, чем тот, который описан здесь.ViewModel было создано для отделения логики приложения от пользовательского интерфейса.Однако в данном случае, похоже, это не так, потому что, если я хочу наблюдать значение, мне нужно быть в Activity.Кроме того, я не нашел никакой альтернативы, как сделать один вид получения одного значения из БД.Любая помощь приветствуется.

Ответы [ 5 ]

0 голосов
/ 23 апреля 2019

Подход 1: Вы можете использовать поле автоинкремента или PrimaryKey, например id, для функциональности book_order.Вы даже можете назвать это book_order, если хотите.Сделайте свой класс Model или Entity похожим на:

public class Book{
    @PrimaryKey(autoGenerate = true)
    private int book_order;
    //other data members, constructors and methods
}

. Таким образом, значение book_order увеличивается с каждым добавленным в базу данных Book.

Далее вы можете получить ViewModel classs like:

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();
    }

}

Теперь вы можете подписать свой список Activity на эту ViewModel (т. е. заставить вашу активность наблюдать ViewModel), поместив вызов следующего метода в onCreate():

вашего действия.
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
}

Делая эти вещи, вы сможете получать обновления, т.е.всякий раз, когда пользователь добавляет в вашу базу данных Book, представление Список / Переработчик должно автоматически отображать добавленные Book.

Подход 2: Если это не то, что выЕсли вы хотите, и вам нужно только найти заказ последней добавленной книги, вы можете полностью пропустить третий кодовый блок и использовать следующее в Dao:

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

, что дает общее количество книг.то есть.строки в таблице книг, которые вы можете затем вызывать из своего репозитория, которые, в свою очередь, могут вызываться из вашей ViewModel, которые, в свою очередь, могут вызываться из Activity.

подход 3: Есливы хотите book_order, которое, я думаю, является последним числом книг в базе данных после добавления новой книги, вы можете использовать подход 1, который дает вам список книг в ViewModel.Затем вы можете получить количество книг из списка книг.

Важно! В любом случае вы захотите отредактировать свой метод insertBook() в своем редакторе или в новой модели ViewBook ​​ViewBook ​​и сделать его примерно таким:

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

и в своемХранилище соответствующей вставки будет выглядеть так:

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

и соответствующий метод Дао:

@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertBook(Book book);
0 голосов
/ 23 апреля 2019

Если вы действительно хотите сделать это с LiveData, вы можете создать собственный:

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)
    }
}

Тогда вы можете просто создать его и использовать:

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

Вы все еще можете наблюдать эти данные в реальном времени, так как они публикуют initialList, когда книга добавлена, она уведомит наблюдателей. Вы можете изменить его, например, чтобы вернуть добавленную книгу и т. Д.

Примечание: Возможно, вместо этого лучше расширить с MutableLiveData, поскольку LiveData не должен обновлять его значение, но внутренне вы публикуете что-то, что может сбить с толку.

0 голосов
/ 20 апреля 2019

Использование LiveData не поможет в вашем сценарии.

LiveData в DAO выполняется в другом потоке, а новое значение публикуется в коде наблюдателя или в вашем случае с обратным вызовом Transformation.map (). Так что вам нужно получить доступ к book.id внутри Transformation.map ().

Однако, если вы вставите книгу в Transformation.map (), это вызовет бесконечный цикл, так как при каждом обновлении записи таблицы Transformation.map () будет вызываться, поскольку LiveData> будет меняться.

Итак, для вашего случая:

  1. Предоставить метод, который выставляет идентификатор последней книги
  2. Вставить новую запись.
  3. Добавьте LiveData> для получения обновления и отображения в пользовательском интерфейсе.
0 голосов
/ 23 апреля 2019

Вместо того, чтобы брать все книги для поиска макс. порядка книг , вы должны создать в своем хранилище метод, который предоставит вам максимальное количество book_order из базы данных.

Что-то вроде нижепсевдокод:

int maxOrder = bookRepository.getMaxBookOrder();

Теперь все, что вам нужно сделать, это вставить новую книгу, вы можете использовать эту переменную maxOrder для инкрементной цели.

Итак, ваш метод вставки будетнапример:

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

Здесь, при условии, что вы используете ROOM для постоянная база данных , этот запрос может помочь вам получить максимальный book_order:

SELECT MAX(book_order) FROM Book

Если ROOM не ваш случай, вы можете использовать другой подход:

Сначала мы получаем список с использованием метода репозитория, а затем находим максимум из него, как показано ниже: псевдо:

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

Затем найдите max из него и затем увеличьте его на единицу:

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

Даже вы можете переместить этот код поиска максимума в метод репозитория, как упоминалось ранее, например:

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;
}
0 голосов
/ 15 апреля 2019

Обратите внимание, что ваши books равны livedata, поэтому может время от времени изменять его значение.
Где ваше bookMax - это одиночное значение , которое должно быть рассчитано в момент вставки.

Чтобы вставить вам нужно:

  • получить список текущих книг
  • затем рассчитайте bookMax
  • затем вставьте.
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

РЕДАКТИРОВАТЬ Вот код Java

// 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);
...