JetPack Compose 入门先搞清楚

文章目录

  • 一、相对传统开发而言compose是怎么布局的
    • 一、四块基本区域
    • [二、传统 Android(View / XML 时代)](#二、传统 Android(View / XML 时代))
      • [① 状态栏(StatusBar)](#① 状态栏(StatusBar))
      • [② 内容区(Content)](#② 内容区(Content))
      • [③ 底部导航栏(BottomNavigationView)](#③ 底部导航栏(BottomNavigationView))
      • [④ 虚拟按键栏(Navigation Bar)](#④ 虚拟按键栏(Navigation Bar))
    • [三、Compose(Edge-to-Edge 时代)](#三、Compose(Edge-to-Edge 时代))
      • [① 状态栏(StatusBar)](#① 状态栏(StatusBar))
      • [② 内容区(Content)](#② 内容区(Content))
      • [③ 底部导航栏(App Bottom)](#③ 底部导航栏(App Bottom))
      • [④ 虚拟按键栏(Navigation Bar)](#④ 虚拟按键栏(Navigation Bar))
  • [二、Compose 布局组合是不是类似于 LinearLayout 排列](#二、Compose 布局组合是不是类似于 LinearLayout 排列)
    • [一、为什么你会觉得"像 LinearLayout"?](#一、为什么你会觉得“像 LinearLayout”?)
    • [二、Compose 的布局核心确实是"顺序排列"](#二、Compose 的布局核心确实是“顺序排列”)
    • [三、那为什么说"不是只有 LinearLayout"?(关键)](#三、那为什么说“不是只有 LinearLayout”?(关键))
    • 四、三种最基础布局,对应传统布局
      • [1️⃣ Column / Row ------ LinearLayout](#1️⃣ Column / Row —— LinearLayout)
      • [2️⃣ Box ------ FrameLayout](#2️⃣ Box —— FrameLayout)
      • [3️⃣ LazyColumn / LazyRow ------ RecyclerView](#3️⃣ LazyColumn / LazyRow —— RecyclerView)
    • [五、Compose 的"真正不同点"在哪里?(重点)](#五、Compose 的“真正不同点”在哪里?(重点))
    • [六、为什么 Compose 不需要 ConstraintLayout 也能活?](#六、为什么 Compose 不需要 ConstraintLayout 也能活?)
    • [七、你现在可以记住的 4 个"铁律"](#七、你现在可以记住的 4 个“铁律”)
    • 八、最后一句总结(非常关键)
  • 三、开始开发
    • [一、传统 Android 是怎么"开始 UI 的"?](#一、传统 Android 是怎么“开始 UI 的”?)
    • [二、Compose 的 MainActivity 是怎么"开始 UI 的"?](#二、Compose 的 MainActivity 是怎么“开始 UI 的”?)
      • [Compose 在这里做的事(核心变化)](#Compose 在这里做的事(核心变化))
    • [三、`setContent {}` 在 Compose 里的真实含义](#三、setContent {} 在 Compose 里的真实含义)
    • [四、那 `LemonplaypuzzleTheme {}` 是什么?(重点)](#四、那 LemonplaypuzzleTheme {} 是什么?(重点))
      • [传统 Android 的 Theme](#传统 Android 的 Theme)
      • [Compose 的 Theme](#Compose 的 Theme)
    • [五、Theme 在 Compose 中"做了什么"?](#五、Theme 在 Compose 中“做了什么”?)
      • [Compose 使用示例](#Compose 使用示例)
    • 六、开始布局
      • 页面根布局选择说明
      • [1. Scaffold 的情况](#1. Scaffold 的情况)
      • [2. Column 的情况](#2. Column 的情况)
      • [3. 总结选择方法](#3. 总结选择方法)
      • [Compose 常用布局总结](#Compose 常用布局总结)
      • [1. 基础排列](#1. 基础排列)
      • [2. 页面框架](#2. 页面框架)
      • [3. 列表 / 网格](#3. 列表 / 网格)
      • [4. 装饰 / UI 容器](#4. 装饰 / UI 容器)
      • [5. 高级布局](#5. 高级布局)
      • 6、使用原则
      • [Activity 和 Fragment 的布局选择](#Activity 和 Fragment 的布局选择)
      • [1. Activity 使用 Scaffold(主页面示例)](#1. Activity 使用 Scaffold(主页面示例))
      • [2. Fragment 直接用 Column/Row/Box(内容页示例)](#2. Fragment 直接用 Column/Row/Box(内容页示例))
      • [3. 核心思路总结](#3. 核心思路总结)
  • 七、包结构
    • [一、传统 Android(XML + View)的典型包结构](#一、传统 Android(XML + View)的典型包结构)
    • [二、Jetpack Compose 的包结构变化](#二、Jetpack Compose 的包结构变化)
    • [三、为什么 Compose 不再"按 Activity / Fragment 分包"](#三、为什么 Compose 不再“按 Activity / Fragment 分包”)
      • [1. Compose 的核心思想:**UI = 函数**](#1. Compose 的核心思想:UI = 函数)
      • [2. "页面"变成了"功能模块(Feature)"](#2. “页面”变成了“功能模块(Feature)”)

一、相对传统开发而言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 代码是这样:

kotlin 复制代码
Column {
    Text(...)
    Button(...)
}

或者:

kotlin 复制代码
Row {
    Icon(...)
    Text(...)
}

这在传统开发里的直觉映射是:

Compose 传统
Column LinearLayout(vertical)
Row LinearLayout(horizontal)

👉 这是 Compose 的"默认排列方式",所以你的感觉是完全正确的。


二、Compose 的布局核心确实是"顺序排列"

Compose 的第一个心智模型就是:

子项按声明顺序,从上到下 / 从左到右排列

这点和 LinearLayout 一模一样


三、那为什么说"不是只有 LinearLayout"?(关键)

因为 Compose 没有 View 层级成本,所以:

组合方式不是"选一种布局",
而是"随时嵌套、随时切换排列策略"


四、三种最基础布局,对应传统布局

1️⃣ Column / Row ------ LinearLayout

kotlin 复制代码
Column {
    A()
    B()
}

xml 复制代码
<LinearLayout
    android:orientation="vertical">
    <View A/>
    <View B/>
</LinearLayout>

2️⃣ Box ------ FrameLayout

kotlin 复制代码
Box {
    Image(...)
    Text(...)
}

xml 复制代码
<FrameLayout>
    <ImageView/>
    <TextView/>
</FrameLayout>
  • 子项默认重叠
  • 后面的盖在前面

3️⃣ LazyColumn / LazyRow ------ RecyclerView

kotlin 复制代码
LazyColumn {
    items(list) { ... }
}

xml 复制代码
<RecyclerView/>

五、Compose 的"真正不同点"在哪里?(重点)

传统布局思维

先选一个容器(Linear / Relative / Constraint)

再靠属性描述规则


Compose 布局思维

用函数嵌套表达空间关系

例如:

kotlin 复制代码
Column {
    Row {
        Icon()
        Text()
    }
    Divider()
    Button()
}

这不是"选布局",而是:

"这是一个纵向结构,其中第一行是横向结构"


六、为什么 Compose 不需要 ConstraintLayout 也能活?

因为你可以这样组合:

kotlin 复制代码
Column {
    Box {
        Image()
        Text(modifier = Modifier.align(Alignment.BottomStart))
    }
}

👉 组合出来的效果,在传统里你可能要:

  • ConstraintLayout
  • RelativeLayout
  • 或多层嵌套

七、你现在可以记住的 4 个"铁律"

  1. 默认顺序排列(像 LinearLayout)
  2. Box = 重叠容器(像 FrameLayout)
  3. Lazy = 列表(像 RecyclerView)
  4. Modifier = LayoutParams + 属性集合

八、最后一句总结(非常关键)

你对 Compose 的直觉是对的:
它从 LinearLayout 开始,
但不会把你限制在 LinearLayout 里。


三、开始开发

一、传统 Android 是怎么"开始 UI 的"?

你非常熟悉的流程

kotlin 复制代码
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

背后发生的事情(你默认不需要关心)

  1. inflate XML
  2. 创建 View 树
  3. attach 到 Window
  4. 系统开始 measure / layout / draw

👉 UI 是"被加载"的


二、Compose 的 MainActivity 是怎么"开始 UI 的"?

kotlin 复制代码
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            LemonplaypuzzleTheme {
                MainApp()
            }
        }
    }
}

Compose 在这里做的事(核心变化)

  1. 不 inflate XML
  2. 不创建 View 树
  3. 创建一个 Compose UI 宿主
  4. 执行你传入的 Composable 函数

👉 UI 是"被执行出来的"


三、setContent {} 在 Compose 里的真实含义

传统思维对照

kotlin 复制代码
setContentView(R.layout.activity_main)

Compose 等价表达

kotlin 复制代码
setContent {
    MainApp()
}

四、那 LemonplaypuzzleTheme {} 是什么?(重点)

传统 Android 的 Theme

xml 复制代码
<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

kotlin 复制代码
@Composable
fun LemonplaypuzzleTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme

    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        content = content
    )
}

使用方式:

kotlin 复制代码
LemonplaypuzzleTheme {
    MainApp() // 所有 UI 组件都在主题作用域内
}

五、Theme 在 Compose 中"做了什么"?

kotlin 复制代码
MaterialTheme(
    colorScheme = colorScheme,
    typography = Typography,
    content = content
)

等价于传统 Android 的:

  • colors.xml → 定义颜色
  • styles.xml → 定义字体样式
  • textAppearance → 定义文字外观
  • 夜间 / 白天模式切换 → night / day 资源

区别

  • Compose 的 Theme 作用范围只在 MaterialTheme 包裹的 UI 树里
  • 子 Composable 可以随时引用 MaterialTheme.colorSchemeMaterialTheme.typography
  • 可以动态切换颜色 / 字体,而不需要重启 Activity

Compose 使用示例

kotlin 复制代码
@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 就像给页面搭了一个框架,它会帮你自动处理状态栏高度和底部虚拟按键的占位,让页面内容不会被遮挡。

简洁代码示例:

kotlin 复制代码
@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)就够了。

这种布局轻量,完全自定义,适合纯内容页、弹窗页或者子页面。

简洁代码示例:

kotlin 复制代码
@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. 总结选择方法

  1. 页面需要框架?

    • 是 → Scaffold
    • 否 → Column / Row / Box
  2. 页面主要内容怎么排列?

    • 竖直排列 → Column
    • 水平排列 → Row
    • 叠加/层级 → Box
  3. 是否有长列表或网格?

    • 是 → 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、使用原则

  1. 简单排列 → Column / Row / Box
  2. 标准页面框架 → Scaffold
  3. 滚动列表 → Lazy 系列
  4. 复杂约束 → ConstraintLayout / 自定义 Layout
  5. 装饰 / 间距 → Surface / Card / Divider / Spacer

Activity 和 Fragment 的布局选择

一般情况下:

  • Activity :页面级容器 → 用 Scaffold 当根布局
  • Fragment :内容级容器 → 直接用 Column / Row / Box,不需要 Scaffold

1. Activity 使用 Scaffold(主页面示例)

kotlin 复制代码
@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(内容页示例)

kotlin 复制代码
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. 核心思路总结

  1. Activity 是整个页面 → Scaffold
  2. Fragment 只是内容块 → Column/Row/Box
  3. Scaffold 内部仍然可以用 Column/Row/Box 排列具体内容
  4. Fragment 内部如果需要列表/网格 → LazyColumn / LazyRow / LazyVerticalGrid

七、包结构

一、传统 Android(XML + View)的典型包结构

text 复制代码
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 的包结构变化

text 复制代码
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 = 函数

kotlin 复制代码
@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 推崇:

text 复制代码
ui/
 └── home/
     ├── HomeScreen.kt
     ├── HomeViewModel.kt
     ├── HomeState.kt

优势:

  • 一个功能的所有代码在同一个目录
  • 可独立维护、重构、删除
  • 非常适合 多模块 / 大型项目

这是典型的 Feature-based Architecture,而不是传统的 Layer-based。


相关推荐
REDcker25 分钟前
Android WebView 版本升级方案详解
android·音视频·实时音视频·webview·js·编解码
麦兜*27 分钟前
【springboot】图文详解Spring Boot自动配置原理:为什么@SpringBootApplication是核心?
android·java·spring boot·spring·spring cloud·tomcat
le16161637 分钟前
Android 依赖种类及区别:远程仓库依赖、打包依赖、模块依赖、本地仓库依赖
android
lxysbly37 分钟前
psp模拟器安卓版带金手指
android
云上凯歌1 小时前
02 Spring Boot企业级配置详解
android·spring boot·后端
hqiangtai2 小时前
Android 高级专家技术能力图谱
android·职场和发展
aqi002 小时前
FFmpeg开发笔记(九十七)国产的开源视频剪辑工具AndroidVideoEditor
android·ffmpeg·音视频·直播·流媒体
stevenzqzq2 小时前
Android Koin 注入入门教程
android·kotlin
炼金术2 小时前
SkyPlayer v1.1.0 - 在线视频播放功能更新
android·ffmpeg
用户276038157812 小时前
鲲鹏+昇腾:开启 AI for Science 新范式——基于PINN的流体仿真加速实践
android