Android compose

34 阅读3分钟

在 Jetpack Compose 中,响应式适配主要通过以下几种方式实现。我将通过具体例子来说明:

一、基于屏幕配置的响应式布局

1. 使用 LocalConfiguration 获取屏幕信息

@Composable
fun ResponsiveLayout() {
    val configuration = LocalConfiguration.current
    val screenWidth = configuration.screenWidthDp.dp
    val screenHeight = configuration.screenHeightDp.dp
    
    Box(modifier = Modifier.fillMaxSize()) {
        if (screenWidth > 600.dp) {
            // 大屏幕布局
            LargeScreenContent()
        } else {
            // 小屏幕布局
            SmallScreenContent()
        }
    }
}

@Composable
fun LargeScreenContent() {
    Row(modifier = Modifier.fillMaxSize()) {
        Sidebar(modifier = Modifier.width(200.dp))
        MainContent(modifier = Modifier.weight(1f))
    }
}

@Composable
fun SmallScreenContent() {
    Column(modifier = Modifier.fillMaxSize()) {
        TopBar()
        MainContent(modifier = Modifier.weight(1f))
        BottomNavigation()
    }
}

2. 监听屏幕方向变化

@Composable
fun OrientationLayout() {
    val configuration = LocalConfiguration.current
    val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
    
    if (isLandscape) {
        Row {
            Image(modifier = Modifier.weight(1f), painter = painterResource(R.drawable.image))
            Text(modifier = Modifier.weight(1f), text = "横屏内容")
        }
    } else {
        Column {
            Image(painter = painterResource(R.drawable.image))
            Text(text = "竖屏内容")
        }
    }
}

二、Material Design 3 的 WindowSizeClass

1. 基本用法

@Composable
fun AdaptiveContent() {
    WindowSizeClass { windowSizeClass ->
        when (windowSizeClass.widthSizeClass) {
            WindowWidthSizeClass.Compact -> CompactContent()
            WindowWidthSizeClass.Medium -> MediumContent()
            WindowWidthSizeClass.Expanded -> ExpandedContent()
        }
    }
}

@Composable
fun CompactContent() {
    // 手机竖屏布局
    Column {
        Text(text = "紧凑布局")
        // ...
    }
}

@Composable
fun MediumContent() {
    // 平板或手机横屏布局
    Row {
        Text(text = "中等布局")
        // ...
    }
}

@Composable
fun ExpandedContent() {
    // 大屏设备布局
    Row {
        Sidebar()
        MainArea()
        DetailPanel()
    }
}

2. 高度方向的适配

@Composable
fun HeightAdaptiveContent() {
    WindowSizeClass { windowSizeClass ->
        val heightClass = windowSizeClass.heightSizeClass
        
        when (heightClass) {
            WindowHeightSizeClass.Compact -> {
                // 矮屏设备(如折叠屏折叠状态)
                LazyColumn { items(3) { ItemCard() } }
            }
            WindowHeightSizeClass.Medium -> {
                // 中等高度
                LazyColumn { items(5) { ItemCard() } }
            }
            WindowHeightSizeClass.Expanded -> {
                // 高屏设备
                LazyColumn { items(8) { ItemCard() } }
            }
        }
    }
}

三、基于密度的动态缩放

1. 使用 LocalDensity 动态调整尺寸

@Composable
fun DensityAwareLayout() {
    val density = LocalDensity.current
    val context = LocalContext.current
    
    // 根据屏幕密度动态计算
    val baseSize = with(density) { 100.dp.toPx() }
    val adjustedSize = baseSize * getScaleFactor(context)
    
    Box(
        modifier = Modifier
            .width(with(density) { adjustedSize.toDp() })
            .height(50.dp)
    ) {
        Text(text = "自适应尺寸")
    }
}

fun getScaleFactor(context: Context): Float {
    val metrics = context.resources.displayMetrics
    return metrics.density / 2.0f // 相对于 xxhdpi 的缩放
}

2. 自定义密度感知组件

@Composable
fun AdaptiveText(text: String) {
    val configuration = LocalConfiguration.current
    val screenWidth = configuration.screenWidthDp
    
    // 根据屏幕宽度动态调整字体大小
    val textSize = when {
        screenWidth < 360 -> 14.sp
        screenWidth < 600 -> 16.sp
        else -> 18.sp
    }
    
    Text(
        text = text,
        fontSize = textSize,
        modifier = Modifier.padding(16.dp)
    )
}

四、ConstraintLayout 组合

1. 百分比约束

@Composable
fun PercentLayout() {
    ConstraintLayout(modifier = Modifier.fillMaxSize()) {
        val (box1, box2, box3) = createRefs()
        
        Box(
            modifier = Modifier
                .constrainAs(box1) {
                    start.linkTo(parent.start)
                    end.linkTo(parent.end)
                    top.linkTo(parent.top)
                    width = Dimension.percent(1f)
                    height = Dimension.percent(0.3f)
                }
                .background(Color.Blue)
        )
        
        Box(
            modifier = Modifier
                .constrainAs(box2) {
                    start.linkTo(parent.start)
                    top.linkTo(box1.bottom)
                    width = Dimension.percent(0.5f)
                    height = Dimension.percent(0.4f)
                }
                .background(Color.Red)
        )
        
        Box(
            modifier = Modifier
                .constrainAs(box3) {
                    start.linkTo(box2.end)
                    top.linkTo(box1.bottom)
                    end.linkTo(parent.end)
                    height = Dimension.percent(0.4f)
                }
                .background(Color.Green)
        )
    }
}

五、折叠屏适配

1. 检测折叠状态

@Composable
fun FoldableLayout() {
    val windowInfo = rememberWindowInfo()
    val isFolded = windowInfo.windowLayoutInfo.foldingFeatures.isNotEmpty()
    
    if (isFolded) {
        // 折叠状态布局
        FoldedLayout()
    } else {
        // 展开状态布局
        ExpandedLayout()
    }
}

@Composable
fun FoldedLayout() {
    Column(modifier = Modifier.fillMaxSize()) {
        ContentAboveFold()
        // 注意折叠区域可能不可用
        Spacer(modifier = Modifier.height(100.dp))
        ContentBelowFold()
    }
}

@Composable
fun ExpandedLayout() {
    Row(modifier = Modifier.fillMaxSize()) {
        LeftPanel(modifier = Modifier.weight(1f))
        RightPanel(modifier = Modifier.weight(1f))
    }
}

2. 响应式导航

@Composable
fun ResponsiveNavigation() {
    WindowSizeClass { windowSizeClass ->
        val isLargeScreen = windowSizeClass.widthSizeClass >= WindowWidthSizeClass.Medium
        
        if (isLargeScreen) {
            // 大屏使用侧边栏导航
            NavigationRail {
                NavigationRailItem(icon = { Icon(Icons.Default.Home) }, label = { Text("首页") })
                NavigationRailItem(icon = { Icon(Icons.Default.Search) }, label = { Text("搜索") })
            }
        } else {
            // 小屏使用底部导航
            NavigationBar {
                NavigationBarItem(icon = { Icon(Icons.Default.Home) }, label = { Text("首页") })
                NavigationBarItem(icon = { Icon(Icons.Default.Search) }, label = { Text("搜索") })
            }
        }
    }
}

六、实用工具函数

1. 屏幕尺寸判断

@Composable
fun isTablet(): Boolean {
    val configuration = LocalConfiguration.current
    return configuration.screenWidthDp >= 600
}

@Composable
fun isLargeScreen(): Boolean {
    val configuration = LocalConfiguration.current
    return configuration.screenWidthDp >= 900
}

2. 自适应 Padding

@Composable
fun adaptivePadding(): PaddingValues {
    val configuration = LocalConfiguration.current
    return when {
        configuration.screenWidthDp < 360 -> PaddingValues(8.dp)
        configuration.screenWidthDp < 600 -> PaddingValues(16.dp)
        else -> PaddingValues(24.dp)
    }
}

@Composable
fun MyContent() {
    Column(modifier = Modifier.padding(adaptivePadding())) {
        Text(text = "自适应内边距")
    }
}

总结

Compose 的响应式适配核心在于:

  1. 屏幕配置感知:通过 LocalConfiguration 获取屏幕尺寸、方向等信息
  2. 窗口大小分类:使用 WindowSizeClass 进行设备分类
  3. 动态布局切换:根据不同条件渲染不同的布局组件
  4. 密度自适应:通过 LocalDensity 实现像素级的动态调整

这种方式相比传统 View 体系更加灵活,能够更好地应对多样化的设备形态。