单击 RecyclerView 项目时 LiveData 不更新 UI

发布于 2025-01-13 05:45:51 字数 6970 浏览 0 评论 0原文

我有一个杂货清单,正在加载到我的分段回收器视图中,在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦在深巷 2025-01-20 05:45:51

一切看起来都很好。除了我认为可能存在的问题之外,视图模型类中的 listedGroceriesLiveData 而不是 MutableLiveData。尝试使用 MutableLiveData 看看问题是否仍然存在。

Everything looks fine. Except for one thing I suppose might be the issue is that listedGroceries in View Model class is LiveData rather than MutableLiveData. Try to use MutableLiveData see if the problem still happens.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文