You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
}
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 22:55:28