文章目录
- 一、相对传统开发而言compose是怎么布局的
- 二、Compose 布局组合是不是类似于 LinearLayout 排列
- 三、开始开发
- 七、包结构
一、相对传统开发而言compose是怎么布局的
- 传统:树是“XML 静态结构”,XML 一写好结构就固定了
- Compose:树是“函数执行结果”,条件一变,结构就变
一、四块基本区域
┌──────────────────────────┐
│ ① 系统状态栏 StatusBar │ ← 系统画的
├──────────────────────────┤
│ ② App 内容区域 Content │ ← 你画的
├──────────────────────────┤
│ ③ App 底部导航栏 Bottom │ ← 你画的
├──────────────────────────┤
│ ④ 系统虚拟按键栏 NavBar │ ← 系统画的
└──────────────────────────┘
二、传统 Android(View / XML 时代)
系统帮你把 ① 和 ④ 切掉,
你只需要在 ② 里管好 ③。
① 状态栏(StatusBar)
- 属于系统
- 系统自动扣除高度
- 你的
match_parent永远不会盖住它
② 内容区(Content)
- 你真正能布局的区域
- XML 中的根布局默认就在这里
- 你对它有完全控制权
③ 底部导航栏(BottomNavigationView)
- 属于 App UI
- 你要自己加
- 系统不会管它
- 你通过
layout_marginBottom处理内容避让
④ 虚拟按键栏(Navigation Bar)
- 属于系统
- 系统自动避让
- 不需要你操心
三、Compose(Edge-to-Edge 时代)
系统不再替你“切屏”,
四块区域全部同时存在在同一张画布上。
① 状态栏(StatusBar)
- 仍然属于系统
- ❌ 系统不再自动避让
- 内容可以直接画到它下面甚至后面
👉 状态栏不再“占空间”,而是“盖在上面”
② 内容区(Content)
- 不再是“默认安全区域”
- 是你主动决定剩下哪块是内容
- 本质是:
屏幕 −(你自己让出来的区域)
③ 底部导航栏(App Bottom)
-
仍然属于 App UI
-
但现在:
- 高度不是你猜
- 而是布局系统测量
④ 虚拟按键栏(Navigation Bar)
- 仍然属于系统
- ❌ 不再自动避让
- 会直接盖在你内容上
二、Compose 布局组合是不是类似于 LinearLayout 排列
是的,但不只是。
Compose 的布局组合,≈ LinearLayout + FrameLayout + ConstraintLayout 的“能力合集”,
只是默认更像 LinearLayout。
一、为什么你会觉得“像 LinearLayout”?
因为你最常看到的 Compose 代码是这样:
Column {
Text(...)
Button(...)
}
或者:
Row {
Icon(...)
Text(...)
}
这在传统开发里的直觉映射是:
| Compose | 传统 |
|---|---|
Column | LinearLayout(vertical) |
Row | LinearLayout(horizontal) |
👉 这是 Compose 的“默认排列方式”,所以你的感觉是完全正确的。
二、Compose 的布局核心确实是“顺序排列”
Compose 的第一个心智模型就是:
子项按声明顺序,从上到下 / 从左到右排列
这点和 LinearLayout 一模一样。
三、那为什么说“不是只有 LinearLayout”?(关键)
因为 Compose 没有 View 层级成本,所以:
组合方式不是“选一种布局”,
而是“随时嵌套、随时切换排列策略”
四、三种最基础布局,对应传统布局
1️⃣ Column / Row —— LinearLayout
Column {
A()
B()
}
≈
<LinearLayout
android:orientation="vertical">
<View A/>
<View B/>
</LinearLayout>
2️⃣ Box —— FrameLayout
Box {
Image(...)
Text(...)
}
≈
<FrameLayout>
<ImageView/>
<TextView/>
</FrameLayout>
- 子项默认重叠
- 后面的盖在前面
3️⃣ LazyColumn / LazyRow —— RecyclerView
LazyColumn {
items(list) { ... }
}
≈
<RecyclerView/>
五、Compose 的“真正不同点”在哪里?(重点)
传统布局思维
先选一个容器(Linear / Relative / Constraint)
再靠属性描述规则
Compose 布局思维
用函数嵌套表达空间关系
例如:
Column {
Row {
Icon()
Text()
}
Divider()
Button()
}
这不是“选布局”,而是:
“这是一个纵向结构,其中第一行是横向结构”
六、为什么 Compose 不需要 ConstraintLayout 也能活?
因为你可以这样组合:
Column {
Box {
Image()
Text(modifier = Modifier.align(Alignment.BottomStart))
}
}
👉 组合出来的效果,在传统里你可能要:
- ConstraintLayout
- RelativeLayout
- 或多层嵌套
七、你现在可以记住的 4 个“铁律”
- 默认顺序排列(像 LinearLayout)
- Box = 重叠容器(像 FrameLayout)
- Lazy = 列表(像 RecyclerView)
- Modifier = LayoutParams + 属性集合
八、最后一句总结(非常关键)
你对 Compose 的直觉是对的:
它从 LinearLayout 开始,
但不会把你限制在 LinearLayout 里。
三、开始开发
一、传统 Android 是怎么“开始 UI 的”?
你非常熟悉的流程
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
背后发生的事情(你默认不需要关心)
- inflate XML
- 创建 View 树
- attach 到 Window
- 系统开始 measure / layout / draw
👉 UI 是“被加载”的
二、Compose 的 MainActivity 是怎么“开始 UI 的”?
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
LemonplaypuzzleTheme {
MainApp()
}
}
}
}
Compose 在这里做的事(核心变化)
- 不 inflate XML
- 不创建 View 树
- 创建一个 Compose UI 宿主
- 执行你传入的 Composable 函数
👉 UI 是“被执行出来的”
三、setContent {} 在 Compose 里的真实含义
传统思维对照
setContentView(R.layout.activity_main)
Compose 等价表达
setContent {
MainApp()
}
四、那 LemonplaypuzzleTheme {} 是什么?(重点)
传统 Android 的 Theme
<style name="AppTheme" parent="Theme.Material3">
<item name="colorPrimary">#6200EE</item>
<item name="colorOnPrimary">#FFFFFF</item>
</style>
<application
android:theme="@style/AppTheme">
...
</application>
- 主题在 Application / Manifest 里声明
- 全局生效
- 静态配置,通过 XML 控制颜色、字体、样式
Compose 的 Theme
@Composable
fun LemonplaypuzzleTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
使用方式:
LemonplaypuzzleTheme {
MainApp() // 所有 UI 组件都在主题作用域内
}
五、Theme 在 Compose 中“做了什么”?
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
等价于传统 Android 的:
colors.xml→ 定义颜色styles.xml→ 定义字体样式textAppearance→ 定义文字外观- 夜间 / 白天模式切换 → night / day 资源
区别:
- Compose 的 Theme 作用范围只在 MaterialTheme 包裹的 UI 树里
- 子 Composable 可以随时引用
MaterialTheme.colorScheme或MaterialTheme.typography - 可以动态切换颜色 / 字体,而不需要重启 Activity
Compose 使用示例
@Composable
fun Greeting() {
Text(
text = "Hello Compose",
color = MaterialTheme.colorScheme.onBackground,
style = MaterialTheme.typography.bodyLarge
)
}
@Composable
fun MyScreen() {
LemonplaypuzzleTheme {
Greeting() // Greeting 会自动使用主题颜色和字体
}
}
Greeting()的颜色和字体都自动继承LemonplaypuzzleTheme包裹的 MaterialTheme- 如果在另一棵 UI 树中换一个 Theme,对应颜色和字体会立即生效
六、开始布局
页面根布局选择说明
有时候我们会看到页面一开始直接用 Scaffold,有时候会看到直接用 Column。那我们应该怎么判断选哪个呢?
1. Scaffold 的情况
当你的页面有 顶部 AppBar、底部导航栏或者悬浮按钮 FAB 时,就应该用 Scaffold。
Scaffold 就像给页面搭了一个框架,它会帮你自动处理状态栏高度和底部虚拟按键的占位,让页面内容不会被遮挡。
简洁代码示例:
@Composable
fun MainAppWithScaffold() {
Scaffold(
topBar = { TopAppBar(title = { Text("首页") }) },
bottomBar = { BottomNavigationBar() }
) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding) // 自动避开顶部/底部栏
) {
Text("页面主体内容")
}
}
}
- Scaffold → 页面框架
- Column → 页面内容排列
- innerPadding → 系统自动计算的状态栏/底部导航占位
2. Column 的情况
如果页面只是 简单排列内容,没有顶部 AppBar 或底部导航栏,直接用 Column(或者 Row / Box)就够了。
这种布局轻量,完全自定义,适合纯内容页、弹窗页或者子页面。
简洁代码示例:
@Composable
fun SimplePage() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp), // 页面内部间距
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text("标题")
Row {
Text("水平排列控件1")
Text("水平排列控件2")
}
}
}
- Column → 根布局,竖直排列控件
- Row → 水平排列控件
- 轻量,不依赖 Scaffold
3. 总结选择方法
-
页面需要框架?
- 是 → Scaffold
- 否 → Column / Row / Box
-
页面主要内容怎么排列?
- 竖直排列 → Column
- 水平排列 → Row
- 叠加/层级 → Box
-
是否有长列表或网格?
- 是 → LazyColumn / LazyRow / LazyVerticalGrid
Compose 常用布局总结
1. 基础排列
| 组件 | 类似传统 | 作用 |
|---|---|---|
| Column | LinearLayout(垂直) | 垂直排列子控件 |
| Row | LinearLayout(水平) | 水平排列子控件 |
| Box | FrameLayout | 子控件叠加布局 |
2. 页面框架
| 组件 | 类似传统 | 作用 |
|---|---|---|
| Scaffold | CoordinatorLayout + AppBar + BottomNavigation + FrameLayout | 页面整体框架,自动处理状态栏/底部导航占位 |
3. 列表 / 网格
| 组件 | 类似传统 | 作用 |
|---|---|---|
| LazyColumn | RecyclerView(竖向) | 可滚动竖向列表,按需渲染子项 |
| LazyRow | RecyclerView(横向) | 可滚动横向列表 |
| LazyVerticalGrid | RecyclerView + GridLayoutManager | 可滚动网格布局 |
4. 装饰 / UI 容器
| 组件 | 类似传统 | 作用 |
|---|---|---|
| Surface | CardView / FrameLayout | 背景、圆角、阴影 |
| Card | MaterialCardView | 卡片式 UI |
| Divider | View 分割线 | 分隔内容 |
| Spacer | 空 View | 占位、间距 |
5. 高级布局
| 组件 | 作用 |
|---|---|
| ConstraintLayout | 复杂约束布局,支持链式、比例、对齐 |
| BoxWithConstraints | 获取父布局尺寸,做响应式布局 |
| Layout / SubcomposeLayout | 自定义布局,完全控制测量与摆放 |
6、使用原则
- 简单排列 → Column / Row / Box
- 标准页面框架 → Scaffold
- 滚动列表 → Lazy 系列
- 复杂约束 → ConstraintLayout / 自定义 Layout
- 装饰 / 间距 → Surface / Card / Divider / Spacer
Activity 和 Fragment 的布局选择
一般情况下:
- Activity:页面级容器 → 用 Scaffold 当根布局
- Fragment:内容级容器 → 直接用 Column / Row / Box,不需要 Scaffold
1. Activity 使用 Scaffold(主页面示例)
@Composable
fun MainActivityPage() {
Scaffold(
topBar = { TopAppBar(title = { Text("首页") }) },
bottomBar = { BottomNavigationBar() }
) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding) // 避开状态栏/底部导航占位
) {
Text("Activity 页面内容")
}
}
}
因为:
- Activity 是整个页面的容器,有 AppBar/BottomBar/FAB → 用 Scaffold。
- Column/Row/Box 只在 Scaffold 内部排列内容。
2. Fragment 直接用 Column/Row/Box(内容页示例)
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View = ComposeView(requireContext()).apply {
setContent {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
Text("Fragment 内容")
Row {
Text("水平控件1")
Text("水平控件2")
}
}
}
}
}
因为:
- Fragment 已经是页面的一部分了,不需要 Scaffold 框架。
- 直接用 Column/Row/Box 排列控件即可。
3. 核心思路总结
- Activity 是整个页面 → Scaffold
- Fragment 只是内容块 → Column/Row/Box
- Scaffold 内部仍然可以用 Column/Row/Box 排列具体内容
- Fragment 内部如果需要列表/网格 → LazyColumn / LazyRow / LazyVerticalGrid
七、包结构
一、传统 Android(XML + View)的典型包结构
com.example.app
├── ui
│ ├── MainActivity.kt
│ ├── LoginActivity.kt
│ └── adapters/
├── fragment
│ ├── HomeFragment.kt
│ └── ProfileFragment.kt
├── viewmodel
│ └── MainViewModel.kt
├── model
│ └── User.kt
├── repository
│ └── UserRepository.kt
└── res
├── layout
│ ├── activity_main.xml
│ └── fragment_home.xml
└── values
二、Jetpack Compose 的包结构变化
com.example.app
├── ui
│ ├── home
│ │ ├── HomeScreen.kt
│ │ ├── HomeViewModel.kt
│ │ └── HomeState.kt
│ ├── login
│ │ ├── LoginScreen.kt
│ │ └── LoginViewModel.kt
│ └── components
│ ├── AppButton.kt
│ └── AppTopBar.kt
├── navigation
│ └── AppNavGraph.kt
├── domain
│ └── model/
├── data
│ └── repository/
└── MainActivity.kt
核心变化点
| 维度 | 传统 View | Compose |
|---|---|---|
| UI 定义 | XML | Kotlin 函数 |
| 页面单位 | Activity / Fragment | Screen(Composable) |
| 包组织方式 | 按“组件类型” | 按“业务/功能” |
| UI 复用 | include / style | Composable 组合 |
| 状态管理 | imperative | state-driven |
三、为什么 Compose 不再“按 Activity / Fragment 分包”
1. Compose 的核心思想:UI = 函数
@Composable
fun HomeScreen(state: HomeState) {
Column {
Text(state.title)
Button(onClick = state.onClick) { Text("Confirm") }
}
}
结果:
- UI 本身是函数,不是类
- 不需要 XML、Adapter、ViewHolder
- Fragment 的存在价值大幅下降
➡️ 包结构自然不再围绕 Activity / Fragment
2. “页面”变成了“功能模块(Feature)”
Compose 推崇:
ui/
└── home/
├── HomeScreen.kt
├── HomeViewModel.kt
├── HomeState.kt
优势:
- 一个功能的所有代码在同一个目录
- 可独立维护、重构、删除
- 非常适合 多模块 / 大型项目
这是典型的 Feature-based Architecture,而不是传统的 Layer-based。
1545

被折叠的 条评论
为什么被折叠?



