Compose Codelab 学习 - Compose 中的基本布局

Compose 中的基本布局

  • 官方文档:https://developer.android.google.cn/codelabs/jetpack-compose-layouts?hl=zh-cn#0
演示
kotlin 复制代码
@Composable
fun SearchBar(
    modifier: Modifier = Modifier
) {
    TextField(
        value = "",
        onValueChange = {},
        modifier = modifier
            .fillMaxWidth()
            .heightIn(min = 56.dp),
        leadingIcon = {
            Icon(
                imageVector = Icons.Default.Search,
                contentDescription = null
            )
        },
        placeholder = {
            Text(stringResource(R.string.placeholder_search))
        },
        colors = TextFieldDefaults.colors(
            unfocusedContainerColor = MaterialTheme.colorScheme.surface,
            focusedContainerColor = MaterialTheme.colorScheme.surface
        ),
    )
}
kotlin 复制代码
@Composable
fun AlignYourBodyElement(
    @DrawableRes drawable: Int,
    @StringRes text: Int,
    modifier: Modifier = Modifier
) {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = modifier
    ) {
        Image(
            painter = painterResource(drawable),
            contentDescription = null,
            modifier = Modifier
                .size(88.dp)
                .clip(CircleShape),
            contentScale = ContentScale.Crop,
        )
        Text(
            text = stringResource(text),
            modifier = Modifier.paddingFromBaseline(top = 24.dp, bottom = 8.dp),
        )
    }
}

class AlignYourBodyData(
    @DrawableRes val drawable: Int,
    @StringRes val text: Int,
)

@Composable
fun AlignYourBodyRow(
    alignYourBodyData: List<AlignYourBodyData>,
    modifier: Modifier = Modifier
) {
    LazyRow(
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        contentPadding = PaddingValues(8.dp),
        modifier = modifier
    ) {
        items(alignYourBodyData) { item ->
            AlignYourBodyElement(item.drawable, item.text)
        }
    }
}
kotlin 复制代码
@Composable
fun FavoriteCollectionCard(
    @DrawableRes drawable: Int,
    @StringRes text: Int,
    modifier: Modifier = Modifier
) {
    Surface(
        shape = MaterialTheme.shapes.medium,
        modifier = modifier
    ) {
        Row(
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier.width(255.dp)
        ) {
            Image(
                painter = painterResource(drawable),
                contentDescription = null,
                modifier = Modifier.size(80.dp),
                contentScale = ContentScale.Crop,
            )
            Text(text = stringResource(text), modifier = Modifier.padding(start = 8.dp))
        }
    }
}

class FavoriteCollectionsData(
    @DrawableRes val drawable: Int,
    @StringRes val text: Int,
)

@Composable
fun FavoriteCollectionsGrid(
    favoriteCollectionsData: List<FavoriteCollectionsData>,
    modifier: Modifier = Modifier
) {
    LazyHorizontalGrid(
        rows = GridCells.Fixed(2),
        modifier = modifier.height(208.dp),
        horizontalArrangement = Arrangement.spacedBy(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp),
        contentPadding = PaddingValues(16.dp),
    ) {
        items(favoriteCollectionsData) { item ->
            FavoriteCollectionCard(item.drawable, item.text, modifier = Modifier.height(80.dp))
        }
    }
}
kotlin 复制代码
@Composable
fun HomeSection(
    @StringRes title: Int,
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Column(modifier) {
        Text(
            stringResource(title),
            style = MaterialTheme.typography.titleMedium,
            modifier = Modifier
                .paddingFromBaseline(top = 40.dp, bottom = 16.dp)
                .padding(horizontal = 16.dp)
        )
        content()
    }
}

@Composable
fun HomeScreen(modifier: Modifier = Modifier) {
    Column(
        modifier = modifier.verticalScroll(rememberScrollState()),
    ) {
        Spacer(Modifier.height(16.dp))
        SearchBar(Modifier.padding(horizontal = 16.dp))
        HomeSection(title = R.string.align_your_body) {
            AlignYourBodyRow(
                alignYourBodyData = listOf(
                    AlignYourBodyData(
                        drawable = R.drawable.ab1_inversions,
                        text = R.string.ab1_inversions
                    ),
                    AlignYourBodyData(
                        drawable = R.drawable.ab1_inversions,
                        text = R.string.ab1_inversions
                    ),
                    AlignYourBodyData(
                        drawable = R.drawable.ab1_inversions,
                        text = R.string.ab1_inversions
                    ),
                    AlignYourBodyData(
                        drawable = R.drawable.ab1_inversions,
                        text = R.string.ab1_inversions
                    ),
                    AlignYourBodyData(
                        drawable = R.drawable.ab1_inversions,
                        text = R.string.ab1_inversions
                    ),
                ),
            )
        }
        HomeSection(title = R.string.favorite_collections) {
            FavoriteCollectionsGrid(
                favoriteCollectionsData = listOf(
                    FavoriteCollectionsData(
                        drawable = R.drawable.fc2_nature_meditations,
                        text = R.string.fc2_nature_meditations
                    ),
                    FavoriteCollectionsData(
                        drawable = R.drawable.fc2_nature_meditations,
                        text = R.string.fc2_nature_meditations
                    ),
                    FavoriteCollectionsData(
                        drawable = R.drawable.fc2_nature_meditations,
                        text = R.string.fc2_nature_meditations
                    ),
                    FavoriteCollectionsData(
                        drawable = R.drawable.fc2_nature_meditations,
                        text = R.string.fc2_nature_meditations
                    ),
                    FavoriteCollectionsData(
                        drawable = R.drawable.fc2_nature_meditations,
                        text = R.string.fc2_nature_meditations
                    ),
                ),
            )
        }
        Spacer(Modifier.height(16.dp))
    }
}
kotlin 复制代码
@Composable
fun SootheBottomNavigation(modifier: Modifier = Modifier) {
    NavigationBar(
        modifier = modifier
    ) {
        NavigationBarItem(
            icon = {
                Icon(
                    imageVector = Icons.Default.Spa,
                    contentDescription = null
                )
            },
            label = {
                Text(
                    text = stringResource(R.string.bottom_navigation_home)
                )
            },
            selected = true,
            onClick = {}
        )
        NavigationBarItem(
            icon = {
                Icon(
                    imageVector = Icons.Default.AccountCircle,
                    contentDescription = null
                )
            },
            label = {
                Text(
                    text = stringResource(R.string.bottom_navigation_profile)
                )
            },
            selected = false,
            onClick = {}
        )
    }
}

@Composable
fun SootheNavigationRail(modifier: Modifier = Modifier) {
    NavigationRail(
        modifier = modifier.padding(start = 8.dp, end = 8.dp)
    ) {
        Column(
            modifier = modifier.fillMaxHeight(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            NavigationRailItem(
                icon = {
                    Icon(
                        imageVector = Icons.Default.Spa,
                        contentDescription = null
                    )
                },
                label = {
                    Text(
                        text = stringResource(R.string.bottom_navigation_home)
                    )
                },
                selected = true,
                onClick = {}
            )
            Spacer(modifier = Modifier.height(16.dp))
            NavigationRailItem(
                icon = {
                    Icon(
                        imageVector = Icons.Default.AccountCircle,
                        contentDescription = null
                    )
                },
                label = {
                    Text(
                        text = stringResource(R.string.bottom_navigation_profile)
                    )
                },
                selected = false,
                onClick = {}
            )
        }
    }
}
kotlin 复制代码
@Composable
fun MyApp(windowSize: WindowSizeClass) {
    when (windowSize.widthSizeClass) {
        WindowWidthSizeClass.Compact -> {
            Column {
                HomeScreen(Modifier.weight(1f))
                SootheBottomNavigation()
            }
        }

        WindowWidthSizeClass.Expanded -> {
            Row {
                SootheNavigationRail()
                HomeScreen()
            }
        }
    }
}
1、8dp 网格
  1. 8dp 网格是 Android 界面设计中的一套基础对齐系统,它规定所有UI元素的尺寸和间距都应该是 8dp 的整数倍

  2. 所有组件的大小、边距、内边距和位置,都基于 8dp 这个基本单位。例如,常见的按钮高度为 48dp,应用栏高度为 56dp

2、标准的 Compose 组件
kotlin 复制代码
@Composable
fun AlignYourBodyElement(
    modifier: Modifier = Modifier
) {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = modifier
    ) {
        Image(
            painter = painterResource(R.drawable.ab1_inversions),
            contentDescription = null,
            modifier = Modifier
                .size(88.dp)
                .clip(CircleShape),
            contentScale = ContentScale.Crop,
        )
        Text(
            text = stringResource(R.string.ab1_inversions),
            modifier = Modifier.paddingFromBaseline(top = 24.dp, bottom = 8.dp),
        )
    }
}

@Preview(showBackground = true)
@Composable
fun AlignYourBodyElementPreview() {
    AlignYourBodyElement()
}
kotlin 复制代码
@Composable
fun AlignYourBodyElement(
    @DrawableRes drawable: Int,
    @StringRes text: Int,
    modifier: Modifier = Modifier
) {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = modifier
    ) {
        Image(
            painter = painterResource(drawable),
            contentDescription = null,
            modifier = Modifier
                .size(88.dp)
                .clip(CircleShape),
            contentScale = ContentScale.Crop,
        )
        Text(
            text = stringResource(text),
            modifier = Modifier.paddingFromBaseline(top = 24.dp, bottom = 8.dp),
        )
    }
}

@Preview(showBackground = true)
@Composable
fun AlignYourBodyElementPreview() {
    AlignYourBodyElement(
        drawable = R.drawable.ab1_inversions,
        text = R.string.ab1_inversions,
        modifier = Modifier.padding(8.dp)
    )
}
3、painterResource 函数与 stringResource 函数
(1)基本介绍
  • painterResource 和 stringResource 都是 Jetpack Compose 中用来加载资源的可组合函数
  1. painterResource 函数负责加载图片
kotlin 复制代码
@Composable
fun painterResource(@DrawableRes id: Int): Painter {
    val context = LocalContext.current
    val res = resources()
    val value = remember { TypedValue() }
    res.getValue(id, value, true)
    val path = value.string
    // Assume .xml suffix implies loading a VectorDrawable resource
    return if (path?.endsWith(".xml") == true) {
        val imageVector = loadVectorResource(context.theme, res, id, value.changingConfigurations)
        rememberVectorPainter(imageVector)
    } else {
        // Otherwise load the bitmap resource
        val imageBitmap = remember(path, id, context.theme) {
            loadImageBitmapResource(path, res, id)
        }
        BitmapPainter(imageBitmap)
    }
}
  1. stringResource 函数负责加载文字
kotlin 复制代码
@Composable
@ReadOnlyComposable
fun stringResource(@StringRes id: Int): String {
    val resources = resources()
    return resources.getString(id)
}
  • painterResource 函数和 stringResource 函数是对传统 Android 原生 API 的封装,让它们在 Compose 的世界里使用起来更方便、更符合声明式 UI 的风格
(2)演示
kotlin 复制代码
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description)
)
4、手动添加滚动行为
(1)基本介绍
  1. LazyRow 和 LazyHorizontalGrid 等延迟布局会自动添加滚动行为

  2. 在列表中有许多元素或需要加载大型数据集时,需要使用延迟布局,因此一次发出所有项不仅会降低性能,还会拖慢应用的运行速度

  3. 如果列表中的元素数量有限,也可以选择使用简单的 Column 或 Row,然后手动添加滚动行为

  4. 可以使用 verticalScroll 或 horizontalScroll 修饰符。这些修饰符需要 ScrollState,后者包含当前的滚动状态,可用于从外部修改滚动状态

  5. 使用 rememberScrollState 创建一个持久的 ScrollState 实例

kotlin 复制代码
fun Modifier.verticalScroll(
    state: ScrollState,
    enabled: Boolean = true,
    flingBehavior: FlingBehavior? = null,
    reverseScrolling: Boolean = false
) = scroll(
    state = state,
    isScrollable = enabled,
    reverseScrolling = reverseScrolling,
    flingBehavior = flingBehavior,
    isVertical = true
)
复制代码
fun Modifier.horizontalScroll(
    state: ScrollState,
    enabled: Boolean = true,
    flingBehavior: FlingBehavior? = null,
    reverseScrolling: Boolean = false
) = scroll(
    state = state,
    isScrollable = enabled,
    reverseScrolling = reverseScrolling,
    flingBehavior = flingBehavior,
    isVertical = false
)
(2)演示
kotlin 复制代码
Column(
    modifier = Modifier.verticalScroll(rememberScrollState()),
) {
    ...
}
5、导航栏
kotlin 复制代码
@Composable
fun SootheBottomNavigation(modifier: Modifier = Modifier) {
    NavigationBar(
        modifier = modifier
    ) {
        NavigationBarItem(
            icon = {
                Icon(
                    imageVector = Icons.Default.Spa,
                    contentDescription = null
                )
            },
            label = {
                Text(
                    text = stringResource(R.string.bottom_navigation_home)
                )
            },
            selected = true,
            onClick = {}
        )
        NavigationBarItem(
            icon = {
                Icon(
                    imageVector = Icons.Default.AccountCircle,
                    contentDescription = null
                )
            },
            label = {
                Text(
                    text = stringResource(R.string.bottom_navigation_profile)
                )
            },
            selected = false,
            onClick = {}
        )
    }
}
  • 适配横屏模式下的显示效果
kotlin 复制代码
@Composable
fun SootheNavigationRail(modifier: Modifier = Modifier) {
    NavigationRail(
        modifier = modifier.padding(start = 8.dp, end = 8.dp)
    ) {
        Column(
            modifier = modifier.fillMaxHeight(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            NavigationRailItem(
                icon = {
                    Icon(
                        imageVector = Icons.Default.Spa,
                        contentDescription = null
                    )
                },
                label = {
                    Text(
                        text = stringResource(R.string.bottom_navigation_home)
                    )
                },
                selected = true,
                onClick = {}
            )
            Spacer(modifier = Modifier.height(16.dp))
            NavigationRailItem(
                icon = {
                    Icon(
                        imageVector = Icons.Default.AccountCircle,
                        contentDescription = null
                    )
                },
                label = {
                    Text(
                        text = stringResource(R.string.bottom_navigation_profile)
                    )
                },
                selected = false,
                onClick = {}
            )
        }
    }
}
相关推荐
后台模板学习2 小时前
学习的心态高频面试题
java·数据库·学习
用户3126874877202 小时前
线程池到底怎么管理线程的?从 ThreadPoolExecutor 到拒绝策略全链路拆解
java
律宏阔2 小时前
Android 手机通过 adblib 使用 ADB Wi-Fi 控制 Android 9 开发板
android
律宏阔3 小时前
Android App 里实现开机动画替换
android
cpolar技术支持3 小时前
Kafka Streams 窗口统计怎么验收:本地跑订单流聚合,用 cpolar 给同事看只读结果页
java·docker·kafka·cpolar·kafka streams
爱读源码的大都督3 小时前
DeepSeek面试官问:生产RAG系统回答不准确,该如何定位和优化?这样回答,能让面试官当场给你Offer!
java·后端·python
消失的旧时光-19433 小时前
Android 系统层扫盲 05:Android 开机后发生了什么?从 Bootloader 到 Launcher
android·zygote·fork·aosp·cow
杨运交3 小时前
[069][公共模块]Spring Boot 全局异常处理与参数校验实战(下):校验异常精细化处理与 WebFlux 适配
java·spring boot·后端
Raas1004 小时前
MAI Gateway(魔芋企业级AI网关)对比分析:AI网关和OpenRouter区别?企业级能力差距一览
java·服务器·网络·人工智能·gateway·ai网关·mai gateway
APItesterCris4 小时前
告别人工盯品!借助 Open‑Claw 快速搭建电商商品全自动监控与数据分析系统(完整实操代码)
java·大数据·前端·数据库