Kotlin特别记录

Kotlin 学习

学什么
Day 1Kotlin 基础 + 空安全
Day 2Lambda / data class / 扩展
Day 3when / sealed class
Day 4协程 Coroutine
Day 5Compose 基础
Day 6Compose + ViewModel
Day 7写一个完整小 App

推荐技术栈总表

分类官方推荐
语言Kotlin
UIJetpack Compose
架构MVVM
状态StateFlow
异步Coroutine
数据库Room
本地存储DataStore
网络Retrofit + OkHttp
DIHilt
导航Navigation Compose

Modifier 理解

  • Modifier = 给 Composable 叠加行为和外观的“装饰链”,就像:CSS、Vue 的 :style + :class、Flutter 的 Widget 包 Widget

  • Modifier 使用

    1
    2
    3
    4
    	Modifier
    .fillMaxWidth()
    .padding(16.dp)
    .background(Color.Red)```
  • 特点

    • 链式调用
    • 有顺序
    • 不可变(每一步返回新 Modifier)
  • Modifier 作用

    • 布局(Layout)
1
2
3
4
	Modifier
.width(100.dp)
.height(50.dp)
.fillMaxWidth()
- 外观(Draw)
1
2
3
	Modifier
.background(Color.Blue)
.clip(RoundedCornerShape(8.dp))
- 行为(Interaction)
1
2
Modifier
.clickable { }
- 手势 & 输入
1
2
3
    .pointerInput(Unit) {
detectTapGestures { }
}
    • Modifier 顺序 = 生命线,顺序不同,效果完全不同
    • 示例1
1
2
3
4
5
6
7
8
Modifier
.background(Color.Red)
.padding(16.dp)

// 情况 B
Modifier
.padding(16.dp)
.background(Color.Red)

区别 - A:背景包含 padding - B:背景不包含 padding

- 示例2 clickable 放哪?
1
2
3
4
5
6
7
8
// 情况 A
Modifier
.padding(16.dp)
.clickable { }
//情况B
Modifier
.clickable { }
.padding(16.dp)

区别 - A:点击区域包含 padding - 点击区域不包含 padding(很坑)

  • Modifier 的常用套路
    • 标准按钮写法(推荐)
1
2
   .fillMaxWidth()
.padding(horizontal = 16.dp)
- 卡片样式
1
2
3
4
   .fillMaxWidth()
.padding(8.dp)
.clip(RoundedCornerShape(8.dp))
.background(Color.White)
- 点击 + Ripple
1
2
3
4
5
	Modifier
.clickable(
indication = rememberRipple(),
interactionSource = remember { MutableInteractionSource() }
) { }

LocalContext理解 val context = LocalContext.current

  • LocalContext:是一个 CompositionLocal 对象,用于在 Compose 树中隐式传递 Android Context
    • .current:获取当前 Composable 函数所在位置的 Context 值
    • 在 Compose 中,你不能直接使用 Activity.this 或 applicationContext,因为:Compose 函数不是传统的 View 或 Activity,需要一种在组件树中安全传递 Context 的机制
    • 用法:
      1. 获取资源
1
2
3
4
5
6
7
8
9
10
val context = LocalContext.current

// 获取字符串
val appName = context.getString(R.string.app_name)

// 获取颜色
val primaryColor = context.getColor(R.color.primary)

// 获取尺寸
val spacing = context.resources.getDimension(R.dimen.spacing_small)
2. 启动 Activity:context.startActivity(Intent(context, DetailActivity::class.java))
3. 显示 Toast :Toast.makeText(context, "消息", Toast.LENGTH_SHORT).show()
4. 访问系统服务:val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator

rememberCoroutineScope 解析,val scope = rememberCoroutineScope()

1.  概念:rememberCoroutineScope 是 Jetpack Compose 中获取与当前 Composable 生命周期绑定的协程作用域的关键函数。val scope = rememberCoroutineScope()
2.  核心特性
	1.  生命周期感知
		  - 自动取消:当 Composable 离开组合(退出屏幕/销毁)时,作用域会自动取消
		  - 避免泄漏:防止协程在 Composable 销毁后继续运行

	2.   响应重组
		  - 记住状态:在重组期间保持同一个协程作用域实例
		  - 不会重复创建:避免不必要的资源开销
3.  基本用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Composable
fun ExampleScreen() {
val scope = rememberCoroutineScope()

Button(
onClick = {
scope.launch {
// 执行异步操作
delay(1000)
// 更新 UI 状态(需在协程内)
}
}
) {
Text("执行任务")
}
}
  1. 处理点击事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Composable
fun SearchButton() {
val scope = rememberCoroutineScope()
var results by remember { mutableStateOf<List<String>>(emptyList()) }

Column {
Button(
onClick = {
scope.launch {
// 发起网络请求
results = performSearch()
}
}
) {
Text("搜索")
}

// 显示结果
results.forEach { Text(it) }
}
}

suspend fun performSearch(): List<String> {
delay(2000) // 模拟网络请求
return listOf("结果1", "结果2")
}
  1. 启动动画
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Composable
fun AnimatedBox() {
val scope = rememberCoroutineScope()
var animated by remember { mutableStateOf(false) }

Box(
modifier = Modifier
.size(if (animated) 200.dp else 100.dp)
.background(Color.Blue)
.clickable {
scope.launch {
animated = !animated
delay(1000) // 动画延时
animated = !animated
}
}
)
}
  1. 收集 Flow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Composable
fun FlowCollector() {
val scope = rememberCoroutineScope()
var value by remember { mutableStateOf(0) }

// 在副作用中启动协程
LaunchedEffect(Unit) {
scope.launch {
flow {
repeat(10) {
emit(it)
delay(1000)
}
}.collect {
value = it
}
}
}

Text("当前值: $value")
}

LaunchedEffect 解析

1. 概念:LaunchedEffect 是 Compose 中用于在进入组合时自动执行挂起函数的副作用 API。
1
2
3
LaunchedEffect(key1, key2, ...) {
// 进入组合时自动执行的协程代码块
}
  1. 核心特性
  • 自动启动
    • 无需手动触发:进入组合时自动执行
    • 一次性执行:默认只在首次组合时执行一次
  • 生命周期绑定
    • 进入组合时启动:当 Composable 显示时启动协程
    • 退出组合时取消:当 Composable 离开组合时自动取消协程
    • key 变化时重启:当依赖的 key 变化时,会重启协程
  1. 使用场景
    • 场景 1:初始化数据加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Composable
fun UserProfileScreen(userId: String) {
var userData by remember { mutableStateOf<User?>(null) }
var isLoading by remember { mutableStateOf(true) }

// 当 userId 变化时重新加载数据
LaunchedEffect(userId) {
isLoading = true
userData = userRepository.getUser(userId)
isLoading = false
}

if (isLoading) {
LoadingIndicator()
} else {
UserProfile(user = userData)
}
}
  • 场景 2:监听 Flow 或 StateFlow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Composable
fun TimerScreen() {
var currentTime by remember { mutableStateOf(0L) }

// 监听时间 Flow
LaunchedEffect(Unit) { // Unit 表示只执行一次
timerFlow
.onEach { time ->
currentTime = time
}
.collect()
}

Text("时间: $currentTime")
}
  • 场景 3:执行动画
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Composable
fun FadeInContent() {
var alpha by remember { mutableStateOf(0f) }

LaunchedEffect(Unit) {
// 进入时执行渐入动画
animate(0f, 1f) { value, _ ->
alpha = value
delay(16) // 约 60fps
}
}

Box(
modifier = Modifier
.fillMaxSize()
.graphicsLayer { this.alpha = alpha }
) {
Text("渐入内容")
}
}
  • 场景 4:清理资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Composable
fun WebSocketConnection() {
LaunchedEffect(Unit) {
val socket = openWebSocket()

try {
socket.collect { message ->
// 处理消息
}
} finally {
// ✅ 确保连接关闭
socket.close()
}
}
}
  • 场景 5:加载 + 刷新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Composable
fun UserDetailScreen(userId: String) {
var user by remember { mutableStateOf<User?>(null) }

// 初始加载
LaunchedEffect(userId) {
user = loadUser(userId)
}

// UI 包含刷新按钮
UserDetail(
user = user,
onRefresh = {
// 使用 rememberCoroutineScope 手动刷新
}
)
}
  • 场景 6:轮询数据
1
2
3
4
5
6
7
8
9
10
11
@Composable
fun LiveDataScreen() {
var liveData by remember { mutableStateOf<Data?>(null) }

LaunchedEffect(Unit) {
while (true) {
liveData = fetchLiveData()
delay(5000) // 每 5 秒轮询一次
}
}
}