如何使用Jetpack Compose实现平板横屏模式下的双窗格布局
基于Jetpack Compose实现平板横屏双窗格布局方案
1. 前置依赖准备
首先添加窗口大小判断和双窗格组件的官方依赖,Module级build.gradle配置如下:
dependencies { // 窗口大小类别判断依赖 implementation "androidx.compose.material3:material3-window-size-class:1.2.0" // 可选:滑动双窗格组件依赖,支持手势、折叠屏适配 implementation "androidx.slidingpanelayout:slidingpanelayout-compose:1.2.0" }
2. 宽屏状态识别
不要硬编码判断设备类型,用官方窗口大小类适配所有宽屏场景(平板横屏、折叠屏展开、桌面模式、分屏宽窗口等):
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { val windowSizeClass = calculateWindowSizeClass(this) val isWideLandscape = windowSizeClass.widthSizeClass == WindowWidthSizeClass.Expanded && resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE MyAppTheme { if (isWideLandscape) { // 横屏宽窗口加载双窗格 TwoPaneScreen() } else { // 其他情况加载单窗格 SinglePaneScreen() } } } } }
3. 双窗格布局实现
方案1:基础Row实现(简单场景够用)
适合固定比例分割的双窗格,不需要滑动交互的场景:
@Composable fun TwoPaneScreen( modifier: Modifier = Modifier, selectedItemId: Int? = null, onItemSelect: (Int) -> Unit = {} ) { Row(modifier = modifier.fillMaxSize()) { // 左侧列表窗格,占30%宽度 ListPane( modifier = Modifier.weight(0.3f), selectedId = selectedItemId, onItemClick = onItemSelect ) // 右侧详情窗格,占70%宽度 DetailPane( modifier = Modifier.weight(0.7f), itemId = selectedItemId ) } } @Composable fun ListPane( modifier: Modifier = Modifier, selectedId: Int?, onItemClick: (Int) -> Unit ) { Column( modifier = modifier .fillMaxHeight() .background(MaterialTheme.colorScheme.surfaceVariant) .padding(16.dp) ) { Text("列表窗格", style = MaterialTheme.typography.headlineSmall) // 替换为你的列表逻辑,点击回调选中ID } } @Composable fun DetailPane(modifier: Modifier = Modifier, itemId: Int?) { Column( modifier = modifier .fillMaxHeight() .background(MaterialTheme.colorScheme.background) .padding(16.dp) ) { Text("详情窗格", style = MaterialTheme.typography.headlineSmall) // 替换为你的详情逻辑,根据itemId加载内容 } }
方案2:SlidingPaneLayout实现(推荐,适配性更强)
支持手势滑动隐藏左侧窗格、折叠屏适配、自动适配窗口大小变化:
@Composable fun AdaptiveTwoPaneScreen( selectedItemId: Int? = null, onItemSelect: (Int) -> Unit = {} ) { val slidingPaneState = rememberSlidingPaneState() SlidingPaneLayout( state = slidingPaneState, paneWidth = 320.dp, // 左侧窗格固定宽度,也可自定义比例 modifier = Modifier.fillMaxSize() ) { ListPane(selectedId = selectedItemId, onItemClick = onItemSelect) DetailPane(itemId = selectedItemId) } }
4. 双窗格状态联动
如果需要点击左侧列表更新右侧内容,用状态提升或者ViewModel共享状态即可:
// 示例用ViewModel全局共享选中状态 class SharedViewModel : ViewModel() { private val _selectedItemId = MutableStateFlow<Int?>(null) val selectedItemId: StateFlow<Int?> = _selectedItemId.asStateFlow() fun selectItem(id: Int) { _selectedItemId.value = id } } // 父组件调用 @Composable fun TwoPaneRoute( viewModel: SharedViewModel = viewModel() ) { val selectedId by viewModel.selectedItemId.collectAsStateWithLifecycle() TwoPaneScreen( selectedItemId = selectedId, onItemSelect = { viewModel.selectItem(it) } ) }
注意事项
- 配置变更时(比如横竖屏切换),用
rememberSaveable或者ViewModel保存状态,避免数据丢失 - 如果需要支持分屏、折叠屏展开折叠的实时布局切换,不需要额外处理,窗口大小类会自动回调更新布局
内容的提问来源于stack exchange,提问作者Gnmnm
相关产品推荐
相关产品推荐

