从 LiveData 获取一个值

Get one value from LiveData

我在 ViewModel 的构造函数中有 LiveData 书籍:

LiveData<List<Book>> books;

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

当用户从 UI 创建新的 book 时,我希望属性 book_order 填充其他图书的最大值 book_order。为了更好地描述我想要什么,请参阅以下 preudocode:

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

所以当有三本书 book_order 分别为 1, 2, 3 时,新书会将此属性设置为 4。

问题是,如何使用 ViewModel 中的 LiveData 执行此操作?我尝试使用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 的创建是为了将应用程序逻辑与 UI 分开。但是在这种情况下它似乎并没有这样做,因为如果我想观察值,我需要在 Activity 中。此外,我没有找到任何替代方法来执行这种从数据库获取一个值的操作。任何帮助表示赞赏。

请注意,您的 bookslivedata,因此可能 不时更改 其值。
Whereis 你的 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);

使用 LiveData 对您的情况没有帮助。

DAO 中的 LiveData 在不同的线程上执行,新值发布在观察者代码中,或者在您的情况下 Transformation.map() 回调。所以你需要在 Transformation.map() 中访问 book.id。

但是,如果您在 Transformation.map() 中插入书籍,它会触发无限循环,因为在每个 table 条目更新时,Transformation.map() 都会被调用,因为 LiveData> 会发生变化。

所以,对于你的情况:

  1. 公开一个公开最后一本书 ID 的方法
  2. 插入一个新条目。
  3. 添加 LiveData> 以接收更新并在 UI 中显示。

与其获取所有书籍以查找最大 图书订单,不如在您的存储库中创建一个方法,该方法将从数据库中为您提供最大数量的 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

然后从中求出最大值然后加一:

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

如果你真的想用 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,它会通知观察者。您可以更改更多,例如,更改为 return 添加的书等

旁注: 最好从 MutableLiveData 扩展,因为 LiveData 不应该更新它的值,但在内部你是发布一些东西可能会造成混淆。

方法一: 您可以使用自动递增字段或 PrimaryKey(例如 id)来实现 book_order 的功能。如果愿意,您甚至可以将其命名为 book_order。让您的模型或实体 class 喜欢:

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

因此,book_order 在每个 Book 添加到数据库时递增。

接下来你可以让你的 ViewModel class 像:

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 的 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 添加到您的数据库时,List/Recycler 视图应自动显示新添加的 Book.

方法二: 如果这不是您想要的,您只想找到最新添加的图书的订单,您可以完全跳过第三个代码块并在 Dao:

中使用以下代码
@Query("SELECT COUNT(*) FROM books")
int getCount();

这给出了书籍的总数,即。书籍 table 中的行,然后您可以从您的存储库中调用这些行,这又可以从您的 ViewModel 中调用,而 ViewModel 又可以从 Activity.

中调用

方法三: 如果你想要 book_order ,我认为这是添加新书后数据库中的最新书籍数量,你可以使用方法 1,它为你提供 ViewModel 中的书籍列表。然后,您可以从书单计数中获取书籍数量。

重要! 无论哪种方式,您都希望在编辑器或 newBook ViewModel 中编辑 insertBook() 方法,并使其类似于:

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