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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 13:36:03