Android Kotlin:如何用单个Data Class适配ViewPager双Tab数据
问题描述
App中有一个带两个Tab的ViewPager Fragment:
- 第一个Tab展示全部数据,调用
getAllLinks接口 - 第二个Tab展示Top10数据,调用
getTopTen接口
两个接口定义如下:
@GET("api/get-all") suspend fun getAllLinks(@Query("userid") userID: String, @Query("token") token: String, @Query("page") page: Int) : Respond @GET("api/topten") suspend fun getTopTen(@Query("userid") userid: String, @Query("token")token: String): Respond
统一使用的Respond数据类:
data class Respond( val error: List<ErrorMsg>?, val data: List<DataContent>?, val token: String?, )
问题现象:仅全部数据Tab能正常显示,Top10数据无法展示。但新建RespondTop数据类并修改getTopTen接口返回类型后,两个Tab数据都能正常显示。现在要求无需新建数据类,用单个Respond类同时处理两个接口的返回数据。
补充代码:
AllDataViewModel的getData方法
fun getData(userid: String, token: String, page: Int){ val job = viewModelScope.launch { _loading.value = true try { val getData = Network.networkService.getAll(userid, token, page) _respond.value = getData _loading.value = false _error.value = false _isCancelled.value = false }catch (e: Exception){ Log.e("AllLinksException", e.message.toString()) _loading.value = false if(e.message.toString() == "StandaloneCoroutine was cancelled"){ _error.value = false _isCancelled.value = true } else { _error.value = true _isCancelled.value = false } } } resetValue() runningJob = job }
AllLink Fragment观察数据代码
viewModel.respond.observe(viewLifecycleOwner) { it?.let { binding.shimmer.visibility = View.GONE binding.rvLinks.visibility = View.VISIBLE Utils.sharedPreferenceString(sharedPreferences, "token", it.token.toString()) token = it.token.toString() if (it.error?.get(0)?.invalidToken != null) { Utils.showToast(requireContext(), it.error.get(0).invalidToken.toString()) } else { for (i in 0..it?.data!!.size - 1) { links.add( CurrentLink( it.data.get(i).ID.toString(), it.data.get(i).title.toString(), it.data.get(i).createdDate.toString() ) ) } totalLink = it.data.get(0).totalLink!!.toInt() binding.tvTotalLinks.text = totalLink.toString() linkAdapter.notifyDataSetChanged() } page++ } }
TopData ViewModel的getData方法
fun getData(userid: String, token: String){ viewModelScope.launch { try { val data = Network.networkService.getTopTen(userid, token) _respond.value = data }catch (e: Exception){ Log.e("Top Ten Exception", e.message.toString()) } } }
TopTenFragment代码
binding = FragmentTopBinding.inflate(inflater) viewModel = ViewModelProvider(this).get(TopDataViewModel::class.java) sharedPreferences = requireActivity().getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE) val userid = sharedPreferences.getString("userid", null) val token = sharedPreferences.getString("token", null) viewModel.getData(userid.toString(), token.toString()) val adapter = TopTenAdapter() binding.rvLinks.adapter = adapter viewModel.respond.observe(viewLifecycleOwner){ Log.i("Data Link", "jkfdjksf") Log.i("Data Link Top", it.toString()) binding.shimmer.visibility = View.GONE binding.rvLinks.visibility = View.VISIBLE if(it?.error!!.get(0).invalidToken != null){ Utils.showToast(requireContext(), it.error.get(0).invalidToken.toString()) } else { Log.i("Data Link Top", it.toString()) } } return binding.root
解决方案
问题根源是两个接口返回的data字段结构存在差异(比如Top10接口返回的DataContent没有totalLink字段),加上代码中存在非空强制调用、数据未绑定到适配器等问题,导致Top10数据无法正常展示。以下是具体修复步骤:
1. 适配DataContent的可选字段
将DataContent中的totalLink改为可空类型,避免解析或访问时触发空指针:
data class DataContent( // 保留原有其他字段 val ID: String, val title: String, val createdDate: String, val totalLink: String? // 改为可空,适配Top10接口无此字段的情况 )
2. 修复TopTenFragment的数据绑定逻辑
当前TopTenFragment仅打印日志但未将数据绑定到适配器,同时错误处理存在空指针风险,修改后代码如下:
viewModel.respond.observe(viewLifecycleOwner){ respond -> respond?.let { Log.i("Data Link Top", it.toString()) binding.shimmer.visibility = View.GONE // 安全处理错误信息 it.error?.firstOrNull()?.invalidToken?.let { errorMsg -> Utils.showToast(requireContext(), errorMsg) binding.rvLinks.visibility = View.GONE return@let } // 绑定数据到适配器 binding.rvLinks.visibility = View.VISIBLE it.data?.let { dataList -> val topTenItems = dataList.map { item -> CurrentLink( item.ID.toString(), item.title.toString(), item.createdDate.toString() ) } adapter.submitList(topTenItems) // 使用submitList更安全,自动更新UI } ?: run { Utils.showToast(requireContext(), "暂无Top10数据") } } }
3. 移除AllLinkFragment中的非空强制调用
避免因Top10接口逻辑影响All接口,将AllLinkFragment中的非空强制调用改为安全处理:
// 原代码 totalLink = it.data.get(0).totalLink!!.toInt() // 修改后 it.data?.firstOrNull()?.totalLink?.toIntOrNull()?.let { total -> totalLink = total binding.tvTotalLinks.text = total.toString() } ?: run { binding.tvTotalLinks.text = "0" }
4. 确保ViewModel的LiveData独立性
确认两个ViewModel的_respond是各自独立的MutableLiveData实例,避免数据互相覆盖:
// TopDataViewModel中的LiveData定义示例 private val _respond = MutableLiveData<Respond?>() val respond: LiveData<Respond?> = _respond
内容的提问来源于stack exchange,提问作者Jugs
相关产品推荐
相关产品推荐

