Accompanist Pager+Tabs组合时Composable提前组合引发API冗余调用问题
解决Accompanist Pager预加载相邻页面API调用的问题
问题场景
使用Accompanist Pager搭配Tabs实现类似Instagram的关注者/关注对象/订阅标签页时,点击第一个标签会触发相邻第二个标签的Composable组合,导致不必要的API请求提前执行,希望仅在用户点击对应标签时才加载该页面的数据。
问题原因
Accompanist HorizontalPager 默认设置 offscreenLimit = 1,会预加载当前页面左右各1个页面的Composable,导致未激活页面的初始化逻辑(比如LaunchedEffect里的API调用)提前触发。此外,原ViewModel中使用共享状态管理所有标签页的数据,多个页面会互相干扰。
解决方案
1. 关闭Pager预加载
修改HorizontalPager的offscreenLimit参数为0,仅加载当前激活的页面:
HorizontalPager( state = pagerState, itemSpacing = 1.dp, modifier = Modifier.weight(1f), count = tabData.size, offscreenLimit = 0 // 关闭预加载,仅渲染当前页面 ) { index -> Column( modifier = Modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { // 修正索引错误:tabData有3个元素,索引应为0/1/2 when (index) { 0 -> ShowMyFollowing(myDBViewModel = myDBViewModel, isActive = index == pagerState.currentPage) 1 -> ShowMyUsers(myDBViewModel = myDBViewModel, isActive = index == pagerState.currentPage) 2 -> ShowMyFollowers(myDBViewModel = myDBViewModel, isActive = index == pagerState.currentPage) } } }
2. 为每个标签页维护独立状态
在ViewModel中为不同标签页分别创建独立的状态变量,避免状态共享导致的干扰:
class MyDBViewModel : ViewModel() { // FOLLOWING 专属状态 private val _followingPageLoadedTimes = MutableLiveData(0) val followingPageLoadedTimes: LiveData<Int> = _followingPageLoadedTimes private val _followingList = MutableLiveData<List<MyUser>>(emptyList()) val followingList: LiveData<List<MyUser>> = _followingList private val _followingLoading = MutableLiveData(false) val followingLoading: LiveData<Boolean> = _followingLoading // ALLUSERS 专属状态 private val _allUsersPageLoadedTimes = MutableLiveData(0) val allUsersPageLoadedTimes: LiveData<Int> = _allUsersPageLoadedTimes private val _allUsersList = MutableLiveData<List<MyUser>>(emptyList()) val allUsersList: LiveData<List<MyUser>> = _allUsersList private val _allUsersLoading = MutableLiveData(false) val allUsersLoading: LiveData<Boolean> = _allUsersLoading // FOLLOWERS 专属状态 private val _followersPageLoadedTimes = MutableLiveData(0) val followersPageLoadedTimes: LiveData<Int> = _followersPageLoadedTimes private val _followersList = MutableLiveData<List<MyUser>>(emptyList()) val followersList: LiveData<List<MyUser>> = _followersList private val _followersLoading = MutableLiveData(false) val followersLoading: LiveData<Boolean> = _followersLoading // 各标签页对应的API加载方法 fun getFirstPageFollowing() { // 实现FOLLOWING第一页加载逻辑,更新对应状态 } fun getFirstPageAllUsers() { // 实现ALLUSERS第一页加载逻辑,更新对应状态 } fun getFirstPageFollowers() { // 实现FOLLOWERS第一页加载逻辑,更新对应状态 } // 分页加载方法同理,为每个标签页单独实现 }
3. 仅在页面激活时触发API请求
修改页面Composable,添加isActive参数,仅当页面处于激活状态时才执行初始化加载:
@Composable fun ShowMyUsers( myDBViewModel: MyDBViewModel, isActive: Boolean ) { val pageLoadedTimes by myDBViewModel.allUsersPageLoadedTimes.observeAsState(initial = 0) val myUsersList by myDBViewModel.allUsersList.observeAsState(initial = emptyList()) val loading by myDBViewModel.allUsersLoading.observeAsState(initial = false) // 仅当页面激活、数据为空、未加载过且未在加载时,触发API请求 if (isActive && myUsersList.isEmpty() && pageLoadedTimes == 0 && !loading) { LaunchedEffect(key1 = Unit) { Log.d("MP18", "launchedEffect in ShowMyUsers") myDBViewModel.getFirstPageAllUsers() } } ListMyUsers(myUsers = myUsersList, myDBViewModel = myDBViewModel) }
效果说明
通过以上修改,只有用户点击对应标签、页面成为当前激活页时,才会触发该页面的API加载逻辑,彻底避免预加载导致的无效请求,同时每个标签页的状态完全独立,不会互相干扰。
内容的提问来源于stack exchange,提问作者Gregor Sotošek
相关产品推荐
相关产品推荐

