android - Android ViewModel LiveData 观察
问题描述
我正在按照Android 开发团队提供的 ViewModel 实施指南进行操作。但是,我得到了意想不到的结果。我一直在挖掘互联网,但没有运气。
BookMarketPojo.java
class BookMarketPojo {
private String bookTitle;
private String bookAuthor;
private String bookDescription;
private String bookPictureUrl;
private String ownerId;
private boolean sold;
public BookMarketPojo() {
}
public BookMarketPojo(String bookTitle, String bookAuthor, String bookDescription, String bookPictureUrl, String ownerId, boolean sold) {
this.bookTitle = bookTitle;
this.bookAuthor = bookAuthor;
this.bookDescription = bookDescription;
this.bookPictureUrl = bookPictureUrl;
this.ownerId = ownerId;
this.sold = sold;
}
// getters and setters
}
BookViewModel.java
public class BookViewModel extends ViewModel {
private List<BookMarketPojo> booksList = new ArrayList<>();
private final String NODE_NAME = "_books_for_sale_";
private FirebaseDatabase db = FirebaseDatabase.getInstance();
public MutableLiveData<List<BookMarketPojo>> getData() {
fetchData();
MutableLiveData<List<BookMarketPojo>> data = new MutableLiveData<>();
data.setValue(booksList);
return data;
}
private void fetchData() {
db.getReference(NODE_NAME).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
booksList.add(dataSnapshot.getValue(BookMarketPojo.class));
}
@Override
public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
}
BookMarketAdapter.java
public class BookMarketAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private List<BookMarketPojo> bookMarketPojos;
public BookMarketAdapter(Context context, List<BookMarketPojo> bookMarketPojos) {
this.context = context;
this.bookMarketPojos = bookMarketPojos;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(context).inflate(R.layout.book_market_row, parent, false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
final BookMarketPojo book = bookMarketPojos.get(position);
((ViewHolder) viewHolder).title.setText(book.getBookTitle());
((ViewHolder) viewHolder).author.setText(book.getBookAuthor());
((ViewHolder) viewHolder).description.setText(book.getBookDescription());
Picasso.get().load(book.getBookPictureUrl()).into(((ViewHolder) viewHolder).bookImg);
}
@Override
public int getItemCount() {
return bookMarketPojos.size();
}
private class ViewHolder extends RecyclerView.ViewHolder {
TextView title, author, description;
ImageView bookImg;
public ViewHolder(View convertView) {
super(convertView);
title = convertView.findViewById(R.id.tv_title);
author = convertView.findViewById(R.id.tv_author);
description = convertView.findViewById(R.id.tv_description);
bookImg = convertView.findViewById(R.id.iv_bookImg);
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MarketActivity";
private BookMarketAdapter myAdapter;
private RecyclerView recyclerView;
private BookViewModel bookViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.rv_bookMarket);
bookViewModel = new ViewModelProvider(this).get(BookViewModel.class);
bookViewModel.getData().observe(this, books -> {
myAdapter.notifyDataSetChanged();
});
initRecyclerView();
}
public void initRecyclerView(){
myAdapter = new BookMarketAdapter(this, bookViewModel.getData().getValue());
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(myAdapter);
}
}
预期结果:应用程序在启动时打开 MainActivity 并在回收站视图中显示书籍。
实际结果:应用程序在启动时打开 MainActivity 不显示任何内容。当点击返回应用程序关闭。单击应用程序图标打开时,它会启动并显示 MainActivity,显示重复的书籍数据集。
谁能发现问题出在哪里?为什么 onCreate 不会在第一次初始化适配器?为什么我会得到重复的书单?
解决方案
让我们根据您的问题来分解您所写的内容以及 LiveData 的工作原理,好吗?
首先,您的适配器看起来不错(大部分情况下),这不是问题所在。
让我们看看 ViewModel 以及这里出了什么问题,请阅读我的内联评论以获取解决方案:
public class BookViewModel extends ViewModel {
private List<BookMarketPojo> booksList = new ArrayList<>();
private final String NODE_NAME = "_books_for_sale_";
private FirebaseDatabase db = FirebaseDatabase.getInstance();
/**
* The purpose of livedata is to simply emit events.
* Therefore this can be simply written as [note the general convention used in naming]
*/
public MutableLiveData<List<BookMarketPojo>> onBookListUpdated = MutableLiveData<List<BookMarketPojo>>();
/**
* The problem with this code, is that when you set the value initially, you only set an empty arraylist.
* Remember, your LiveData is only a way to emit changes to a data (the data here being booksList) with the safetynet of LifeCycle Awareness
* therefore, to fetch the data, we only need to do this.
*/
public void getData() {
fetchData()
}
private void fetchData() {
db.getReference(NODE_NAME).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
//To remove duplicates, make sure the book isn't already present in the list. Exercise is left to you
booksList.add(dataSnapshot.getValue(BookMarketPojo.class));
//This will now trigger the livedata, with the list of the books.
onBookListUpdated.setValue(booksList)
}
@Override
public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
}
最后在你的活动的 OnCreate()
bookViewModel.onBookListUpdated.observe(this, books -> {
myAdapter.notifyDataSetChanged();
});
此外,请查看 Firebase Realtime DB Documentation for Reading Data Once,因为我相信它更适合您的用例,而不是只取书一次!
推荐阅读
- go - 如何在 VS Code 中使用参数“./...”调试 Go 测试
- c++ - 未达到的案例会影响开关案例的性能
- python - 如何在同一目录/环境/venv 中构建两个 Kivy 应用程序?
- sql - SUM() OVER (PARTITION BY) AS - 存在重复项时
- python - 从 MATLAB 执行 Powershell 命令
- java - 在tomcat中初始化ServletContext后的回调方法
- regex - 正则表达式匹配类或功能块外的字符串
- visual-studio - 在哪里可以下载 Visual Studio 2019 的主题?
- laravel - Laravel - 使用数据库值验证文本输入值
- excel - 当数组长度小于提供的数组时,excel中“小”函数的处理错误