Android DataBinding中BindingAdapter的LiveData入参始终为null的问题求助
问题根因与修复方案
- ViewModel 作用域错误:详情页Fragment通过
by viewModels()创建的ViewModel是当前Fragment的独立实例,与概览页的ViewModel完全隔离,不存在共享关系。详情页自身的ViewModel实例中的listPlantPhoto、plantPhotoDisplay从初始化开始就没有被赋值,所有绑定值自然为null。 - 异步时序不匹配:概览页观察选中事件时,
retrievePlantList启动协程异步加载文件列表,函数本身没有挂起等待加载完成,紧接着同步执行changeToPlantPhotos(viewModel.mediaPlantList)时,mediaPlantList这个lateinit属性还未被协程赋值,既可能抛出未初始化异常,也可能读取到空值。 - 绑定语法误用:布局中给两个BindingAdapter使用双向绑定标记
@={},但两个Adapter仅实现了单向赋值逻辑,没有声明反向绑定的回调方法,DataBinding解析时会直接忽略绑定表达式,向Adapter传入null参数。 - 空值类型不匹配:
singleImage的入参声明为非空File类型,但绑定的数据源viewModel.plantPhotoDisplay.plantFilePath在初始状态因为LiveData为null会返回null,类型不匹配时DataBinding会直接向Adapter传入null值。
1. 修复ViewModel共享逻辑
共享ViewModel需要将实例获取的作用域调整为两个Fragment共同的父级作用域,不能绑定到Fragment独立作用域。
详情页Fragment中修改ViewModel获取方式:
// 删除原有独立实例的获取代码 // private val viewModel: CollectionOverviewViewModel by viewModels() // 替换为Activity级别的共享作用域 private val viewModel: CollectionOverviewViewModel by activityViewModels() // 如果是NavHost嵌套结构,可替换为导航图作用域的获取方式 // private val viewModel: CollectionOverviewViewModel by navGraphViewModels(R.id.nav_collection)
2. 修复异步加载时序
将异步加载逻辑整理为顺序执行的挂起流程,避免异步任务与同步取值的代码混写。
修改ViewModel中的数据加载逻辑:
suspend fun loadPlantPhotoList(plantIndividual: PlantIndividual) { val dataClassNum = plantIndividual.plantId // 等文件列表加载完成后再执行后续逻辑 val loadedMediaList = context?.getExternalFilesDir("planio/dataclasses/$dataClassNum") ?.listFiles()?.sortedDescending()?.toMutableList() ?: mutableListOf() mediaPlantList = loadedMediaList // 清空旧数据,避免残留 newPhotoList.clear() loadedMediaList.forEach { val file = FileInputStream(it) val inStream = ObjectInputStream(file) val item = inStream.readObject() as PlantPhoto newPhotoList.add(item) } // 数据解析完成后再更新LiveData,触发绑定刷新 _plantPhotoDisplay.postValue(newPhotoList.last()) _listPlantPhoto.postValue(newPhotoList) }
概览页的导航监听逻辑调整为数据加载完成后再执行跳转:
viewModel.navigateToSelectedPlant.observe(viewLifecycleOwner, { if (null != it) { lifecycleScope.launch { // 等待数据加载完成 viewModel.loadPlantPhotoList(it) // 数据准备完成后执行导航 findNavController().navigate( CollectionOverviewFragmentDirections .actionCollectionOverviewFragmentToCollectionIndividualFragment(it) ) } } })
3. 修复BindingAdapter绑定规则
- 将布局中的双向绑定语法
@={}修改为单向绑定@{},匹配Adapter的单向赋值逻辑。 - 调整
singleImage的参数类型为可空,增加空值处理逻辑,避免类型不匹配问题:
@BindingAdapter("singleImage") fun loadImage(imgView: ImageView, imgUrl: File?) { // 空值场景下清理图片,不执行加载逻辑 imgUrl ?: run { Glide.with(imgView.context).clear(imgView) imgView.setImageDrawable(null) return } Glide.with(imgView.context) .load(imgUrl) .apply( RequestOptions() .placeholder(R.drawable.loading_animation) .error(R.drawable.ic_broken_image) ) .into(imgView) }
- 删除详情页Fragment中手动给RecyclerView设置Adapter的代码,
listPhotoData绑定方法会自动操作RecyclerView的Adapter实例,避免双重初始化导致的Adapter引用错乱。
内容的提问来源于stack exchange,提问作者Lisa
相关产品推荐
相关产品推荐

