单击 RecyclerView 项目时 LiveData 不更新 UI
我有一个杂货清单,正在加载到我的分段回收器视图中,在 HashMap 中检查和未检查的项目。
使用 MVVM、Room 数据库,并且正在通过 Coroutine 过渡到 Kotlin
现在,当我单击某个项目时,它会通过我的 ViewModel 类进行更新,并在后台更新数据库。但除非我终止并重新打开应用程序,否则用户界面不会更新。然后,之前单击的所有项目现在都更改为选中或未选中。
父适配器类
class GrocerySectionAdapter(
private val activity: Context,
private var itemList: HashMap<Int, List<GroceryItem>>,
private val viewModel: GroceryListViewModel,
private val lifeCycle: LifecycleOwner
) : RecyclerView.Adapter<GrocerySectionAdapter.MyViewHolder>() {
inner class MyViewHolder(val viewDataBinding: ParentRecyclerViewBinding) :
RecyclerView.ViewHolder(viewDataBinding.root)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): GrocerySectionAdapter.MyViewHolder {
val binding =
ParentRecyclerViewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
if (position == 1)
holder.viewDataBinding.section.text = "Remove checked items"
else holder.viewDataBinding.section.visibility = View.GONE
holder.viewDataBinding.childRecyclerView.apply {
adapter =
itemList[position]?.let { GroceryListAdapter(activity, it, viewModel, lifeCycle) }
}
}
override fun getItemCount(): Int {
return itemList.size
}
fun setGroceries(groceries: HashMap<Int, List<GroceryItem>>) {
this.itemList = groceries
notifyDataSetChanged()
}
}
子适配器类
class GroceryListAdapter(
private val context: Context,
private var groceries: List<GroceryItem>,
private var viewModel: GroceryListViewModel,
private val lifecycleOwner: LifecycleOwner
) :
RecyclerView.Adapter<GroceryListViewHolder>() {
override fun onBindViewHolder(holder: GroceryListViewHolder, position: Int) {
val groceryItem = groceries[position]
holder.bind(groceryItem)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroceryListViewHolder {
val listItemBinding = ListItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
val holder = GroceryListViewHolder(listItemBinding, viewModel, lifecycleOwner)
listItemBinding.lifecycleOwner = holder
listItemBinding.itemClicked = viewModel
holder.lifecycleCreate()
return holder
}
override fun getItemId(position: Int): Long {
return groceries[position].uid.toLong()
}
override fun getItemCount(): Int {
return groceries.size
}
override fun onViewAttachedToWindow(holder: GroceryListViewHolder) {
super.onViewAttachedToWindow(holder)
holder.attachToWindow()
}
}
override fun onViewAttachedToWindow(holder: GroceryListViewHolder) {
super.onViewAttachedToWindow(holder)
holder.attachToWindow()
}
视图模型类
class GroceryListViewModel(application: Application, private val groceryListRepo: GroceryListRepo) :
AndroidViewModel(application) {
private val uiScope = CoroutineScope(Dispatchers.IO)
val listedGroceries = groceryListRepo.listedGroceries as MutableLiveData<HashMap<Int, List<GroceryItem>>>
fun updateGrocery(groceryItem: GroceryItem?) {
uiScope.launch {
groceryListRepo.updateGrocery(groceryItem)
}
}
fun updateGroceries(groceryItems: List<GroceryItem?>?) {
uiScope.launch {
groceryListRepo.updateGroceries(groceryItems)
}
}
fun updateClickedGrocery(groceryItem: GroceryItem) {
groceryItem.isChecked = !groceryItem.isChecked
updateGrocery(groceryItem)
}
}
存储库类
public class GroceryListRepo {
private final LiveData<List<GroceryItem>> groceryItems;
private final MutableLiveData<HashMap<Integer, List<GroceryItem>>> listedGroceries = new MutableLiveData<>();
GroceryItemDao groceryItemDao;
private final Executor mExecutor = Executors.newSingleThreadExecutor();
public GroceryListRepo(GroceryItemDao groceryItemDao) {
this.groceryItemDao = groceryItemDao;
groceryItems = groceryItemDao.getAllListedGroceries();
}
public LiveData<HashMap<Integer, List<GroceryItem>>> getListedGroceries() {
mExecutor.execute(() -> {
List<GroceryItem> checkedGroceries = groceryItemDao.getAllListedCheckedGroceries();
List<GroceryItem> unCheckedGroceries = groceryItemDao.getAllListedUncheckedGroceries();
HashMap<Integer, List<GroceryItem>> mapListedGroceries = new HashMap<>();
if (!checkedGroceries.isEmpty()) {
mapListedGroceries.put(CHECKED_GROCERIES, checkedGroceries);
}
if (!unCheckedGroceries.isEmpty()) {
mapListedGroceries.put(UNCHECKED_GROCERIES, unCheckedGroceries);
}
listedGroceries.postValue(mapListedGroceries);
});
return listedGroceries;
}
public LiveData<List<GroceryItem>> getAllGroceries() {
return groceryItemDao.getAllGroceries();
}
public void updateGrocery(GroceryItem groceryItem) {
groceryItemDao.updateGrocery(groceryItem);
}
public void updateGroceries(List<GroceryItem> groceryItems) {
groceryItemDao.updateGroceries(new GroceryItem[groceryItems.size()]);
}
}
活动类
GroceryItemDao dao = AppDatabase.getAppDataBase(this).groceryItemDao();
GroceryListRepo repo = new GroceryListRepo(dao);
GroceryListFactoryViewModel factory = new GroceryListFactoryViewModel(repo, getApplication());
groceryListViewModel = ViewModelProviders.of(this, factory).get(GroceryListViewModel.class);
GrocerySectionAdapter adapter = new GrocerySectionAdapter(GroceryListActivity.this,
listedGroceries,
groceryListViewModel,
this);
recyclerView.setAdapter(adapter);
groceryListViewModel.getListedGroceries().observe(this,
groceryItems -> {
listedGroceries = groceryItems;
if (groceryItems.isEmpty()) {
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE);
} else {
recyclerView.setVisibility(View.VISIBLE);
emptyView.setVisibility(View.GONE);
adapter.setGroceries(groceryItems);
}
});
}
I've got a grocery list I'm loading onto my sectioned recyclerview, checked and unchecked items in a HashMap.
Using MVVM, Room database, and am in the middle of a transition to Kotlin with Coroutine
Right now when I click an item it's getting updated via my ViewModel class and updating the database in the background. But the UI is not getting updated unless I kill and reopen the application. Then all of the items that were clicked before now change to either checked or unchecked.
Parent adapter class
class GrocerySectionAdapter(
private val activity: Context,
private var itemList: HashMap<Int, List<GroceryItem>>,
private val viewModel: GroceryListViewModel,
private val lifeCycle: LifecycleOwner
) : RecyclerView.Adapter<GrocerySectionAdapter.MyViewHolder>() {
inner class MyViewHolder(val viewDataBinding: ParentRecyclerViewBinding) :
RecyclerView.ViewHolder(viewDataBinding.root)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): GrocerySectionAdapter.MyViewHolder {
val binding =
ParentRecyclerViewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
if (position == 1)
holder.viewDataBinding.section.text = "Remove checked items"
else holder.viewDataBinding.section.visibility = View.GONE
holder.viewDataBinding.childRecyclerView.apply {
adapter =
itemList[position]?.let { GroceryListAdapter(activity, it, viewModel, lifeCycle) }
}
}
override fun getItemCount(): Int {
return itemList.size
}
fun setGroceries(groceries: HashMap<Int, List<GroceryItem>>) {
this.itemList = groceries
notifyDataSetChanged()
}
}
Child adapter class
class GroceryListAdapter(
private val context: Context,
private var groceries: List<GroceryItem>,
private var viewModel: GroceryListViewModel,
private val lifecycleOwner: LifecycleOwner
) :
RecyclerView.Adapter<GroceryListViewHolder>() {
override fun onBindViewHolder(holder: GroceryListViewHolder, position: Int) {
val groceryItem = groceries[position]
holder.bind(groceryItem)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroceryListViewHolder {
val listItemBinding = ListItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
val holder = GroceryListViewHolder(listItemBinding, viewModel, lifecycleOwner)
listItemBinding.lifecycleOwner = holder
listItemBinding.itemClicked = viewModel
holder.lifecycleCreate()
return holder
}
override fun getItemId(position: Int): Long {
return groceries[position].uid.toLong()
}
override fun getItemCount(): Int {
return groceries.size
}
override fun onViewAttachedToWindow(holder: GroceryListViewHolder) {
super.onViewAttachedToWindow(holder)
holder.attachToWindow()
}
}
override fun onViewAttachedToWindow(holder: GroceryListViewHolder) {
super.onViewAttachedToWindow(holder)
holder.attachToWindow()
}
View Model Class
class GroceryListViewModel(application: Application, private val groceryListRepo: GroceryListRepo) :
AndroidViewModel(application) {
private val uiScope = CoroutineScope(Dispatchers.IO)
val listedGroceries = groceryListRepo.listedGroceries as MutableLiveData<HashMap<Int, List<GroceryItem>>>
fun updateGrocery(groceryItem: GroceryItem?) {
uiScope.launch {
groceryListRepo.updateGrocery(groceryItem)
}
}
fun updateGroceries(groceryItems: List<GroceryItem?>?) {
uiScope.launch {
groceryListRepo.updateGroceries(groceryItems)
}
}
fun updateClickedGrocery(groceryItem: GroceryItem) {
groceryItem.isChecked = !groceryItem.isChecked
updateGrocery(groceryItem)
}
}
Repository Class
public class GroceryListRepo {
private final LiveData<List<GroceryItem>> groceryItems;
private final MutableLiveData<HashMap<Integer, List<GroceryItem>>> listedGroceries = new MutableLiveData<>();
GroceryItemDao groceryItemDao;
private final Executor mExecutor = Executors.newSingleThreadExecutor();
public GroceryListRepo(GroceryItemDao groceryItemDao) {
this.groceryItemDao = groceryItemDao;
groceryItems = groceryItemDao.getAllListedGroceries();
}
public LiveData<HashMap<Integer, List<GroceryItem>>> getListedGroceries() {
mExecutor.execute(() -> {
List<GroceryItem> checkedGroceries = groceryItemDao.getAllListedCheckedGroceries();
List<GroceryItem> unCheckedGroceries = groceryItemDao.getAllListedUncheckedGroceries();
HashMap<Integer, List<GroceryItem>> mapListedGroceries = new HashMap<>();
if (!checkedGroceries.isEmpty()) {
mapListedGroceries.put(CHECKED_GROCERIES, checkedGroceries);
}
if (!unCheckedGroceries.isEmpty()) {
mapListedGroceries.put(UNCHECKED_GROCERIES, unCheckedGroceries);
}
listedGroceries.postValue(mapListedGroceries);
});
return listedGroceries;
}
public LiveData<List<GroceryItem>> getAllGroceries() {
return groceryItemDao.getAllGroceries();
}
public void updateGrocery(GroceryItem groceryItem) {
groceryItemDao.updateGrocery(groceryItem);
}
public void updateGroceries(List<GroceryItem> groceryItems) {
groceryItemDao.updateGroceries(new GroceryItem[groceryItems.size()]);
}
}
Activity class
GroceryItemDao dao = AppDatabase.getAppDataBase(this).groceryItemDao();
GroceryListRepo repo = new GroceryListRepo(dao);
GroceryListFactoryViewModel factory = new GroceryListFactoryViewModel(repo, getApplication());
groceryListViewModel = ViewModelProviders.of(this, factory).get(GroceryListViewModel.class);
GrocerySectionAdapter adapter = new GrocerySectionAdapter(GroceryListActivity.this,
listedGroceries,
groceryListViewModel,
this);
recyclerView.setAdapter(adapter);
groceryListViewModel.getListedGroceries().observe(this,
groceryItems -> {
listedGroceries = groceryItems;
if (groceryItems.isEmpty()) {
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE);
} else {
recyclerView.setVisibility(View.VISIBLE);
emptyView.setVisibility(View.GONE);
adapter.setGroceries(groceryItems);
}
});
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
一切看起来都很好。除了我认为可能存在的问题之外,视图模型类中的
listedGroceries
是LiveData
而不是MutableLiveData
。尝试使用MutableLiveData
看看问题是否仍然存在。Everything looks fine. Except for one thing I suppose might be the issue is that
listedGroceries
in View Model class isLiveData
rather thanMutableLiveData
. Try to useMutableLiveData
see if the problem still happens.