从 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
中。此外,我没有找到任何替代方法来执行这种从数据库获取一个值的操作。任何帮助表示赞赏。
请注意,您的 books
是 livedata
,因此可能 不时更改 其值。
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> 会发生变化。
所以,对于你的情况:
- 公开一个公开最后一本书 ID 的方法
- 插入一个新条目。
- 添加 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);
我在 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
中。此外,我没有找到任何替代方法来执行这种从数据库获取一个值的操作。任何帮助表示赞赏。
请注意,您的 books
是 livedata
,因此可能 不时更改 其值。
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> 会发生变化。
所以,对于你的情况:
- 公开一个公开最后一本书 ID 的方法
- 插入一个新条目。
- 添加 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);