JetPack Compose 入门先搞清楚

文章目录

一、相对传统开发而言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传统
ColumnLinearLayout(vertical)
RowLinearLayout(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 个“铁律”

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

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

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


三、开始开发

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

你非常熟悉的流程

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 的”?

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 里的真实含义

传统思维对照

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.colorSchemeMaterialTheme.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. 总结选择方法

  1. 页面需要框架?

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

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

    • 是 → LazyColumn / LazyRow / LazyVerticalGrid

Compose 常用布局总结

1. 基础排列

组件类似传统作用
ColumnLinearLayout(垂直)垂直排列子控件
RowLinearLayout(水平)水平排列子控件
BoxFrameLayout子控件叠加布局

2. 页面框架

组件类似传统作用
ScaffoldCoordinatorLayout + AppBar + BottomNavigation + FrameLayout页面整体框架,自动处理状态栏/底部导航占位

3. 列表 / 网格

组件类似传统作用
LazyColumnRecyclerView(竖向)可滚动竖向列表,按需渲染子项
LazyRowRecyclerView(横向)可滚动横向列表
LazyVerticalGridRecyclerView + GridLayoutManager可滚动网格布局

4. 装饰 / UI 容器

组件类似传统作用
SurfaceCardView / FrameLayout背景、圆角、阴影
CardMaterialCardView卡片式 UI
DividerView 分割线分隔内容
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(主页面示例)

@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. 核心思路总结

  1. Activity 是整个页面 → Scaffold
  2. Fragment 只是内容块 → Column/Row/Box
  3. Scaffold 内部仍然可以用 Column/Row/Box 排列具体内容
  4. 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

核心变化点

维度传统 ViewCompose
UI 定义XMLKotlin 函数
页面单位Activity / FragmentScreen(Composable)
包组织方式按“组件类型”按“业务/功能”
UI 复用include / styleComposable 组合
状态管理imperativestate-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。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值