从LiveData获得一个价值

时间:2019-04-13 14:01:26

标签: android android-room android-livedata android-viewmodel

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

LiveData<List<Book>> books;

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

当用户从用户界面创建新的book时,我希望属性book_order被其他书籍中book_order的最大增量填充。为了更好地描述我想要的内容,请参见以下preudocode:

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的创建是为了将应用程序逻辑与UI分开。但是,在这种情况下似乎并没有这样做,因为如果我想观察价值,就需要进入Activity。此外,我没有找到其他选择方法来实现这种从DB获得价值的方法。任何帮助表示赞赏。

5 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

在您的情况下使用LiveData将无济于事。

DAO中的LiveData在另一个线程上执行,新值发布在观察者代码中,或者在您的情况下,在Transformation.map()回调中发布。因此,您需要在Transformation.map()中访问book.id。

但是,如果将book插入Transformation.map(),则会触发无限循环,因为在每个表条目更新上,都会调用Transformation.map(),因为LiveData>会发生变化。

因此,对于您的情况:

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

答案 2 :(得分:0)

您应该在存储库中建立一个方法,该方法将为您提供最大数量的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;
}

答案 3 :(得分:0)

如果您真的想用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不应更新其值,但是在内部您正在发布内容,因此令人困惑。

答案 4 :(得分:0)

方法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这样的课程:

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

}

现在,您可以通过将对以下方法的调用放在您活动的onCreate()中来为该ViewModel订阅活动列表(即使您的活动遵守ViewModel):

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中调用它,而ViewModel可以从Activity中调用。

方法3: 如果您想要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);
        }
    });
}

以及相应的Dao方法:

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