Android Jetpack Paging3如何切换参数重新加载分页数据?
问题根源
你的核心问题在于PagingSource是一次性绑定参数的实例,切换分类后没有触发新的PagingSource创建,调用refresh()只会复用旧的PagingSource(持有旧参数),自然加载不到新分类的数据。要解决这个问题,必须让参数变化时重新生成PagingSource,并让UI订阅新的分页数据流。
分步解决方案
1. 改造PagingSource:让它绑定动态参数
修改MoviePagingSource,将分类参数(如mediaType)作为构造参数传入,确保每次加载都使用当前参数:
class MoviePagingSource( private val api: MovieApi, private val mediaType: String // 新增:区分电影/剧集的参数 ) : PagingSource<Int, Movie>() { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Movie> { val currentPage = params.key ?: 1 return try { // 用构造传入的参数调用API val response = api.getMovies(mediaType, currentPage) LoadResult.Page( data = response.results, prevKey = if (currentPage == 1) null else currentPage - 1, nextKey = if (response.results.isEmpty()) null else currentPage + 1 ) } catch (e: Exception) { LoadResult.Error(e) } } // 可选:重写getRefreshKey,确保刷新时回到第一页 override fun getRefreshKey(state: PagingState<Int, Movie>): Int? { return 1 } }
2. 改造Repository:根据参数生成新的分页数据流
让Repository提供根据参数创建Pager的方法,确保每次调用都生成新的PagingSource实例:
class MovieRepository(private val api: MovieApi) { fun getMoviePagingData(mediaType: String): Flow<PagingData<Movie>> { return Pager( config = PagingConfig( pageSize = 20, enablePlaceholders = false ), // 每次调用都创建新的PagingSource,绑定当前参数 pagingSourceFactory = { MoviePagingSource(api, mediaType) } ).flow } }
3. 改造ViewModel:用StateFlow驱动参数变化,自动切换数据流
通过MutableStateFlow保存当前分类参数,用flatMapLatest监听参数变化,自动生成新的分页数据流:
class MovieViewModel(private val repo: MovieRepository) : ViewModel() { // 保存当前选中的分类,默认是电影 private val _currentMediaType = MutableStateFlow("movie") val currentMediaType = _currentMediaType.asStateFlow() // 监听分类变化,自动切换分页数据流 val pagingDataFlow = currentMediaType.flatMapLatest { type -> repo.getMoviePagingData(type) }.cachedIn(viewModelScope) // 缓存数据流,避免配置变化时重复请求 // 对外暴露的分类切换方法 fun switchMediaType(newType: String) { if (_currentMediaType.value != newType) { _currentMediaType.value = newType } } }
4. 改造Activity:绑定按钮点击,订阅数据流
点击按钮时只需更新ViewModel的分类参数,订阅的数据流会自动推送新的分页数据:
class MainActivity : AppCompatActivity() { private lateinit var viewModel: MovieViewModel private lateinit var adapter: MovieAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel = ViewModelProvider(this)[MovieViewModel::class.java] adapter = MovieAdapter() recyclerView.adapter = adapter // 订阅分页数据流,自动更新列表 lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.pagingDataFlow.collect { pagingData -> adapter.submitData(pagingData) } } } // 电影按钮点击 btnMovie.setOnClickListener { viewModel.switchMediaType("movie") // 可选:强制立即刷新(一般不需要,数据流切换后会自动加载第一页) // adapter.refresh() } // 剧集按钮点击 btnTv.setOnClickListener { viewModel.switchMediaType("tv") // adapter.refresh() } } }
关键说明
- 为什么这样有效:每次分类变化时,
flatMapLatest会取消旧的分页数据流,重新调用repo.getMoviePagingData()生成新的Pager和PagingSource,新的PagingSource持有最新的分类参数,自然会加载对应数据并重置到第一页。 cachedIn的作用:将分页数据流缓存到ViewModel的作用域中,屏幕旋转等配置变化时,不会重新发起请求,保留当前分页状态。- 避免手动调用
refresh():只要参数变化触发了新的数据流生成,submitData()会自动让adapter重置并加载第一页,无需额外调用refresh()。
内容的提问来源于stack exchange,提问作者Saneen K P
相关产品推荐
相关产品推荐

