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

RecyclerView分页数据切换问题:Profile页帖子与点赞帖切换显示

如何在ProfileFragment中切换显示用户发布帖与点赞帖?

问题描述

我有一个作为用户资料页的Fragment,默认需要显示用户发布的帖子,点击按钮后切换显示用户点赞的帖子。

初始代码在页面创建时能正常显示用户发布的帖子,但添加按钮切换点赞帖的逻辑后出现问题:如果同时在onViewCreated中收集profilePosts和profileLikedPosts两个Flow,进入页面时不会显示用户发布的帖子,只有点击按钮后才会显示点赞帖。

相关代码如下:

Fragment初始代码

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    // 获取用户发布帖
    viewModel.getProfilePosts(profileId)

    val adapter = PostAdapter(this)

    // 收集发布帖并提交给适配器
    viewLifecycleOwner.lifecycleScope.launch {
        viewModel.profilePosts.collect {
            adapter.submitData(it)
        }
    }
}

切换点赞帖的按钮逻辑

private fun getProfileLikesPosts(profileId: Int) {
    viewModel.getProfileLikes(profileId)
}

错误的尝试(同时收集两个Flow)

viewLifecycleOwner.lifecycleScope.launch {
    viewModel.profileLikedPosts.collect {
        adapter.submitData(it)
    }
}

ViewModel相关代码

// 点赞帖的Flow定义
private val _profileLikedPosts =
    MutableStateFlow<PagingData<PostResponse>>(PagingData.empty())
val profileLikedPosts = _profileLikedPosts

// 获取点赞帖的实现
fun getProfileLikes(profileId: Int) {
    viewModelScope.launch {
        repository.getProfileLikedPosts(profileId).cachedIn(viewModelScope).collect {
            _profileLikedPosts.value = it
        }
    }
}

错误原因

  1. Flow提交冲突:同时启动两个协程分别收集profilePosts和profileLikedPosts,并都调用adapter.submitData()。由于profileLikedPosts初始值是PagingData.empty(),它会覆盖profilePosts提交的数据,导致页面初始显示为空。
  2. 无统一数据源控制:切换时没有明确控制哪个Flow的数据提交给适配器,导致逻辑混乱,无法正确切换显示内容。

解决方案

方案一:ViewModel中维护统一的显示Flow(推荐)

在ViewModel中新增一个统一的currentDisplayPosts Flow,用来管理当前要显示的帖子数据,切换时只需更新这个Flow的指向即可,Fragment只需要收集这一个Flow。

修改后的ViewModel代码

private val _profilePosts = MutableStateFlow<PagingData<PostResponse>>(PagingData.empty())
val profilePosts = _profilePosts.asStateFlow()

private val _profileLikedPosts = MutableStateFlow<PagingData<PostResponse>>(PagingData.empty())
val profileLikedPosts = _profileLikedPosts.asStateFlow()

// 统一的显示流,默认初始为空
private val _currentDisplayPosts = MutableStateFlow<PagingData<PostResponse>>(PagingData.empty())
val currentDisplayPosts = _currentDisplayPosts.asStateFlow()

// 初始化加载用户发布帖,并设置为默认显示内容
fun initProfilePosts(profileId: Int) {
    viewModelScope.launch {
        repository.getProfilePosts(profileId).cachedIn(viewModelScope).collect {
            _profilePosts.value = it
            // 初始时将发布帖设置为当前显示内容
            _currentDisplayPosts.value = it
        }
    }
}

// 加载用户点赞帖
fun loadProfileLikes(profileId: Int) {
    viewModelScope.launch {
        repository.getProfileLikedPosts(profileId).cachedIn(viewModelScope).collect {
            _profileLikedPosts.value = it
        }
    }
}

// 切换到显示用户发布帖
fun switchToOwnPosts() {
    _currentDisplayPosts.value = _profilePosts.value
}

// 切换到显示用户点赞帖
fun switchToLikedPosts() {
    _currentDisplayPosts.value = _profileLikedPosts.value
}

修改后的Fragment代码

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val adapter = PostAdapter(this)
    // 初始化加载并显示用户发布帖
    viewModel.initProfilePosts(profileId)

    // 只收集统一的显示流,无需多个协程
    viewLifecycleOwner.lifecycleScope.launch {
        viewModel.currentDisplayPosts.collect {
            adapter.submitData(it)
        }
    }

    // 切换到点赞帖的按钮点击事件
    btnLikedPosts.setOnClickListener {
        // 如果点赞帖还未加载,先触发加载
        if (viewModel.profileLikedPosts.value == PagingData.empty()) {
            viewModel.loadProfileLikes(profileId)
        }
        viewModel.switchToLikedPosts()
    }

    // 切换回发布帖的按钮点击事件
    btnOwnPosts.setOnClickListener {
        viewModel.switchToOwnPosts()
    }
}

方案二:Fragment中管理协程切换收集目标

如果不想修改ViewModel结构,可以在Fragment中用一个Job来管理当前收集数据的协程,切换时取消旧协程,启动新协程收集对应Flow的数据。

修改后的Fragment代码

private var collectJob: Job? = null
private lateinit var adapter: PostAdapter

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    adapter = PostAdapter(this)
    // 初始收集发布帖
    startCollecting(viewModel.profilePosts)
    viewModel.getProfilePosts(profileId)

    // 切换点赞帖按钮
    btnLikedPosts.setOnClickListener {
        viewModel.getProfileLikes(profileId)
        startCollecting(viewModel.profileLikedPosts)
    }

    // 切换发布帖按钮
    btnOwnPosts.setOnClickListener {
        viewModel.getProfilePosts(profileId)
        startCollecting(viewModel.profilePosts)
    }
}

private fun startCollecting(targetFlow: StateFlow<PagingData<PostResponse>>) {
    // 取消之前的收集任务,避免多个Flow同时提交数据
    collectJob?.cancel()
    collectJob = viewLifecycleOwner.lifecycleScope.launch {
        targetFlow.collect {
            adapter.submitData(it)
        }
    }
}

说明

  • 方案一的优势在于将数据源的切换逻辑封装在ViewModel中,Fragment只负责UI交互和数据展示,符合MVVM架构原则,且已加载的数据会被缓存,切换更流畅。
  • 方案二更适合快速修改,但需要手动管理协程生命周期,相对繁琐。

内容的提问来源于stack exchange,提问作者user2896120

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 05:25:15