如何在setOnClickListener内部调用ViewModel中的数据实现重试加载
问题根因
崩溃主要来自两个问题:
- 嵌套lambda默认参数名
it冲突:外层observe回调的it是返回的Resource数据对象,内层点击事件的it是被点击的View控件对象,直接复用写法会拿错对象触发类型转换异常。 - 代码写法的潜在风险:你把重试点击事件的设置放在了
observe的ERROR分支中,每次触发ERROR回调都会重复添加点击监听器,可能导致一次点击触发多次请求;另外代码中用!!强转可空对象也容易触发空指针崩溃。
解决方案
重试逻辑不需要你在点击事件中重复写列表更新代码,只要点击时触发ViewModel重新拉取数据,请求成功后LiveData会自动触发回调,走你已经写好的SUCCESS分支更新列表即可。建议抽离公共方法避免嵌套lambda冲突,同时优化代码稳定性:
步骤1:抽离公共复用方法
// 触发ViewModel请求新闻数据,替换为你ViewModel中实际的请求方法名 private fun loadNews() { homeViewModel.fetchNewsList() } // 成功拿到数据后更新列表 private fun updateNewsList(newsData: List<你的新闻数据类型>?) { newsData ?: return // 空数据直接返回,避免崩溃 // 优化:不要每次成功都新建Adapter,复用现有Adapter只更新数据性能更好 if (newsAdapter == null) { newsAdapter = NewsAdapter(newsData) binding.rvNews.adapter = newsAdapter } else { newsAdapter.setNewData(newsData) // 需在Adapter中新增更新数据的方法,配合DiffUtil使用效果更好 } } // 展示错误页UI private fun showErrorPage() { binding.newsError.tvError.visibility = View.VISIBLE binding.newsError.btnRetry.visibility = View.VISIBLE binding.tvNewsTitle.visibility = View.INVISIBLE } // 隐藏错误页UI private fun hideErrorPage() { binding.newsError.tvError.visibility = View.GONE binding.newsError.btnRetry.visibility = View.GONE binding.tvNewsTitle.visibility = View.VISIBLE }
步骤2:调整事件设置与Observer逻辑
// 初始化时就设置重试按钮点击事件,只设置一次避免重复添加 binding.newsError.btnRetry.setOnClickListener { loadNews() } // 观察ViewModel返回的数据,这里把默认的it重命名为resource,彻底避免参数名冲突 homeViewModel.newsResult.observe(viewLifecycleOwner) { resource -> when (resource.status) { Resource.Status.LOADING -> { hideErrorPage() // 可添加加载中状态逻辑,比如展示骨架屏、加载动画 } Resource.Status.SUCCESS -> { hideErrorPage() updateNewsList(resource.data) } Resource.Status.ERROR -> { showErrorPage() } } } // 页面首次加载触发请求 loadNews()
额外优化建议
- 禁止用
!!强转可空对象,所有可空参数都做好空判断再使用,避免不必要的崩溃。 - Adapter更新数据时建议使用
DiffUtil计算新旧数据差异,只更新变化的Item,大幅提升列表滑动性能。 - 如果不需要保留之前的加载状态,可以在ViewModel的请求方法中先把
newsResult的状态置为LOADING再发起网络请求,保证点击重试后立即展示加载状态。
内容的提问来源于stack exchange,提问作者dazai
相关产品推荐
相关产品推荐

