在 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 的响应式适配核心在于:
- 屏幕配置感知:通过
LocalConfiguration获取屏幕尺寸、方向等信息 - 窗口大小分类:使用
WindowSizeClass进行设备分类 - 动态布局切换:根据不同条件渲染不同的布局组件
- 密度自适应:通过
LocalDensity实现像素级的动态调整
这种方式相比传统 View 体系更加灵活,能够更好地应对多样化的设备形态。