Kotlin: Live data does not change Fragment UI 当数据改变时

Kotlin: Live data does not change Fragment UI when data changes

我很难在 MVVM 模式上使用实时数据。该应用程序应该:

  1. 从 API 获取数据(正确)
  2. 将该数据存储在来自 ViewModel
  3. 实时数据 object 中
  4. 然后fragment调用Observer方法填充recyclerView

问题出在第3点,它什么也没做,我找不到解决方案。

这是相关代码。 (如有遗漏,我会尽快回复)

主要Activity:


class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private val viewModel: SharedViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // Custom button to fetch data from api and log the Live Data value.
        binding.refreshFab.setOnClickListener {
            viewModel.fetchPlayerData()
            Log.d("gabs", "${viewModel.livePlayerlist.value}")
        }
    }
}

视图模型:

class SharedViewModel(app: Application): AndroidViewModel(app) {

//    val playerDao = LaRojaDB.getDatabase(app).playerDao()

    lateinit var playerList: Players
    val livePlayerlist: MutableLiveData<MutableList<Players.PlayersItem>> by lazy {
        MutableLiveData<MutableList<Players.PlayersItem>>()
    }

    fun fetchPlayerData() {
        CoroutineScope(Dispatchers.IO).launch {
            val response = MyService.getLaRojaService().getAllPlayers()
            withContext(Dispatchers.Main) {
                if (response.isSuccessful) {
                    val body = response.body()

                    if(!body.isNullOrEmpty()){
                        playerList = body
                        val playerArrayList = mutableListOf<Players.PlayersItem>()
                        playerList.forEach {
                            playerArrayList.add(it)
                        }
                        livePlayerlist.value = playerList
                    }
                }
            }
        }
    }
}

显示回收器视图的片段:(片段已经显示,我设置了一个 textView 作为标题以确保因为我也是片段的新手。)

class PlayerListFragment : Fragment() {

    private var _binding: FragmentPlayerListBinding? = null
    private val binding get() = _binding!!

    private val model: SharedViewModel by viewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentPlayerListBinding.inflate(inflater, container, false)
        binding.rvPlayerList.layoutManager = LinearLayoutManager(activity)

        ----> // This is the observer that does not update the UI** <----
        model.livePlayerlist.observe( viewLifecycleOwner, {
            binding.rvPlayerList.adapter = PlayerAdapter(it)
        })

        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_player_list, container, false)
    }
}

提前谢谢大家,希望我能最终了解导致问题的原因!

我认为您不需要切换协程上下文。如果我正在审查这段代码,我希望有一些变化:

这应该都在相同的 IO 上下文中。然后你 postValue 到你的 liveData。

fun fetchPlayerData() {
   viewModelScope.launch(Dispatchers.IO) {
      val xx = api.fetch()
      ...
      _playerState.postValue(xx) //see below
   }
}

此外,最好不要公开可变状态,因此您的 ViewModel 不应公开 MutableLiveData(这不应该真的很懒惰)。但是最好把状态封装在一个密封的class:

    //delete this
    val livePlayerlist: MutableLiveData<MutableList<Players.PlayersItem>> by lazy {
        MutableLiveData<MutableList<Players.PlayersItem>>()
    }

应该是:(名字只是伪代码,我不知道这段代码是关于什么的)

sealed class PlayerDataState {
  data class ListAvailable(data: List<Players.PlayersItem>>): PlayerDataState
  object Loading(): PlayerDataState
}

还有你的新 LiveData:

private val _playerState = MutableLiveData<PlayerDataState>()
val playerState: LiveData<PlayerDataState>() get() = _playerState

最后当从 UI 观察时,你只是...

model.playerState.observe(viewLifecycleOwner, {
   when (it) {
     is Loading -> ... 
     is ListAvailable -> binding.rvPlayerList.adapter = PlayerAdapter(it.data)
   }
}