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

Jetpack Compose切换UI状态时如何保存HorizontalPager页面状态

问题:Compose切换UI状态后HorizontalPager无法恢复历史页面索引

我有两个UI状态:FIRST和SECOND。FIRST状态下界面包含HorizontalPager及其他组件;SECOND状态下仅显示简单的Text组件,通过点击Button在两种状态间切换。

需求:切换到SECOND状态时保存HorizontalPager的当前页面索引,切回FIRST状态时,HorizontalPager直接显示之前的页面。但目前切回FIRST状态时,HorizontalPager总是默认打开第0页。

注意:HorizontalPager的当前页面索引仅在Activity处于活跃状态时保存。Activity被销毁(如关闭应用或跳转其他Activity)后,重新创建时HorizontalPager会从第0页开始。

现有代码

MainActivity

class MainActivity : ComponentActivity() {
    @OptIn(ExperimentalFoundationApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ViewObersableTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    val pagerState = rememberPagerState()
                    SaveVariable(pagerState, VariableViewModel())
                }
            }
        }
    }
}

SaveVariable

@Composable
@OptIn(ExperimentalFoundationApi::class)
fun SaveVariable(pagerState: PagerState, viewModel: VariableViewModel) {
    LaunchedEffect(key1 = pagerState) {
        snapshotFlow { pagerState.currentPage }.collect { page ->
            viewModel.currentIndex = page
        }
    }
    LaunchedEffect(key1 = viewModel.setLastCurrentPage) {
        Log.e(">>> LaunchedEffect", "${viewModel.currentIndex}")
    }
    SaveVariableItem(viewModel.currentIndex, viewModel.uiState, pagerState) {
        viewModel.changeUIi()
    }
}

VariableViewModel

class VariableViewModel : ViewModel() {
    var uiState by mutableStateOf(ScreenName.FIRST)
    var currentIndex by mutableStateOf(0)
    var setLastCurrentPage by mutableStateOf(false)
    fun changeUIi() {
        uiState = if (uiState == ScreenName.FIRST) {
            setLastCurrentPage = true
            ScreenName.SECOND
        } else {
            setLastCurrentPage = false
            ScreenName.FIRST
        }
    }
}

ScreenName

enum class ScreenName {
    FIRST,
    SECOND
}

SaveVariableItem

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SaveVariableItem(
    currentIndex: Int,
    uiState: ScreenName,
    pagerState: PagerState,
    changeUiState: () -> Unit,
) {
    Column {
        when (uiState) {
            ScreenName.FIRST -> {
                ScreenFirstView(pagerState)
            }

            ScreenName.SECOND -> {
                ScreenSecondView(currentIndex, "SECOND")
            }
        }
        ChangeUiStateButton(changeUiState)
    }
}

ChangeUiStateButton

@Composable
fun ChangeUiStateButton(changeUiState: () -> Unit) {
    Button(onClick = { changeUiState() }) {
        Text(text = "Add")
    }
}

ScreenFirstView

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ScreenFirstView(pagerState: PagerState) {
    Column(
        modifier = Modifier
            .wrapContentSize()
            .background(Color.Black)
    ) {
        HorizontalPager(
            modifier = Modifier.padding(20.dp),
            pageCount = 10,
            state = pagerState
        ) { page ->
            Text(
                text = "Page: $page",
                modifier = Modifier.fillMaxWidth(),
                color = Color.White
            )
        }
    }
}

ScreenSecondView

@Composable
fun ScreenSecondView(currentIndex: Int, screenImage: String) {
    Column(
        modifier = Modifier
            .wrapContentSize()
            .background(Color.Black)
    ) {
        Text(text = "$currentIndex ==== $screenImage", color = Color.White)
    }
}

使用的Compose版本:

implementation(platform("androidx.compose:compose-bom:2023.05.01"))

问题分析

  1. ViewModel实例创建错误:直接通过VariableViewModel()创建实例,导致ViewModel无法与Activity生命周期绑定,状态可能丢失。
  2. PagerState未主动恢复:虽然ViewModel保存了currentIndex,但切换回FIRST状态时,没有将保存的索引同步到pagerState中,导致HorizontalPager使用初始的0页。
  3. 冗余状态变量:setLastCurrentPage变量无实际作用,增加了逻辑复杂度。

解决方案

1. 正确获取ViewModel实例

在Compose中,使用viewModel()函数获取ViewModel实例,确保其生命周期与宿主组件(Activity)一致:

class MainActivity : ComponentActivity() {
    @OptIn(ExperimentalFoundationApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ViewObersableTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    val viewModel: VariableViewModel = viewModel()
                    // 初始化PagerState时使用ViewModel中保存的索引
                    val pagerState = rememberPagerState(initialPage = viewModel.currentIndex)
                    SaveVariable(pagerState, viewModel)
                }
            }
        }
    }
}

2. 监听UI状态切换,恢复Pager页面

在SaveVariable中添加LaunchedEffect监听uiState变化,当切回FIRST状态时,将保存的索引同步到pagerState:

@Composable
@OptIn(ExperimentalFoundationApi::class)
fun SaveVariable(pagerState: PagerState, viewModel: VariableViewModel) {
    // 监听Pager页面变化,实时保存到ViewModel
    LaunchedEffect(key1 = pagerState) {
        snapshotFlow { pagerState.currentPage }.collect { page ->
            viewModel.currentIndex = page
        }
    }

    // 监听UI状态切换,切回FIRST时恢复页面
    LaunchedEffect(key1 = viewModel.uiState) {
        if (viewModel.uiState == ScreenName.FIRST) {
            // 使用animateScrollToPage实现平滑跳转,无需动画可改用scrollToPage
            pagerState.animateScrollToPage(viewModel.currentIndex)
        }
    }

    SaveVariableItem(viewModel.currentIndex, viewModel.uiState, pagerState) {
        viewModel.changeUIi()
    }
}

3. 简化ViewModel逻辑

移除冗余的setLastCurrentPage变量,简化状态切换逻辑:

class VariableViewModel : ViewModel() {
    var uiState by mutableStateOf(ScreenName.FIRST)
    var currentIndex by mutableStateOf(0)

    fun changeUIi() {
        uiState = if (uiState == ScreenName.FIRST) {
            ScreenName.SECOND
        } else {
            ScreenName.FIRST
        }
    }
}

验证效果

修改完成后,切换到SECOND状态再切回FIRST状态,HorizontalPager会自动跳转到之前停留的页面索引,满足需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 18:24:57