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。


相关推荐
liang_jy2 小时前
Android LaunchMode
android·面试
阿里云云原生3 小时前
Android App 崩溃排查实战:如何利用 RUM 完整数据与符号化技术定位问题?
android·阿里云·云原生·rum
过期动态4 小时前
JDBC高级篇:优化、封装与事务全流程指南
android·java·开发语言·数据库·python·mysql
没有了遇见6 小时前
Android 音乐播放器之MotionLayout实现View流畅变换
android
TheNextByte17 小时前
在 PC 和Android之间同步音乐的 4 种方法
android
君莫啸ོ7 小时前
Android基础-Activity属性 android:configChanges
android
TimeFine7 小时前
Android AI解放生产力(七):更丰富的AI运用前瞻
android
保持低旋律节奏7 小时前
linux——进程状态
android·linux·php
明川8 小时前
Android Gradle - ASM + AsmClassVisitorFactory插桩使用
android·前端·gradle