diff --git a/Extra/我做了一个KMP的FFmpeg库.md b/Extra/我做了一个KMP的FFmpeg库.md index 3d082d6..e749c0d 100644 --- a/Extra/我做了一个KMP的FFmpeg库.md +++ b/Extra/我做了一个KMP的FFmpeg库.md @@ -96,16 +96,4 @@ kotlin { } } } -``` - -## 目前仍存在的问题 - -如果直接在gradle中添加我的依赖,那么,可能会出现这样的问题 - -```bash -C:\Users\32721\AppData\Local\Temp\ffmpegkit_2621802206663535111.dll: %1 不是有效的 Win32 应用程序。 - at java.base/jdk.internal.loader.NativeLibraries.load(Native Method) - ...... -``` - -目前,我还不知道如何解决,但可以确认的是,拿编译输出的 jar 包直接用作依赖不会出现这样的问题。 \ No newline at end of file +``` \ No newline at end of file diff --git a/Learning/Kotlin-Springboot.md b/Learning/Kotlin-Springboot.md new file mode 100644 index 0000000..fb353df --- /dev/null +++ b/Learning/Kotlin-Springboot.md @@ -0,0 +1,7 @@ +# Kotlin Springboot + +> 总体上来讲,Kotlin 与 Java 相比是有好有坏的,优点是写起来更简洁,缺点是写起来更简洁。 + +## 创建一个 Springboot 项目 + +这很简单,在 IDEA 中就可以创建,记得选 Kotlin 和 Gradle kts。 \ No newline at end of file diff --git a/Learning/基本的UI结构.md b/Learning/基本的UI结构.md deleted file mode 100644 index e69de29..0000000 diff --git a/Learning/基本的项目结构.md b/Learning/基本的项目结构.md deleted file mode 100644 index fe5aa07..0000000 --- a/Learning/基本的项目结构.md +++ /dev/null @@ -1,32 +0,0 @@ -# 项目的基本结构 - -## 目录 - -一般来讲,一个 KMP 项目在生成的时候,长这样 - -- / - - androidApp/ - - desktopApp/ - - iosApp/ - - webApp/ (JS 已经死了,现在的是 WAMS) - - shared/ - - src/ - - commonMain/ - - commonTest/ - - androidMain/ - - ...... - - build.gradle.kts - - .gitignore - - README.md - - gradle 二进制文件(三大平台各一个) - - gradle.proporties - - local.proporties - - build.gradle.kts - - settings.gradle.kts - -如果你的项目还要包含 server 的部分的话,那会稍微复杂一点点 - -## 各部分用途 - -所有的共享的部分(如UI,基础逻辑)都写在 `shared/commonMain` 里面,不同平台如果要做不同的效果,那么,相应的代码写在对应的 `Main` 里面,`App` 目录主要控制打包设置和进入软件的窗口设定,虽然可以写针对不同平台的不同效果,但是不推荐。 - diff --git a/Learning/引入Kotin_fast.md b/Learning/引入Kotin_fast.md index 0d2036b..4474a8c 100644 --- a/Learning/引入Kotin_fast.md +++ b/Learning/引入Kotin_fast.md @@ -342,4 +342,6 @@ val a = doOrNull(b) { it.c + 1 } [目录](../README.md) -[下一篇:深入 Kotlin](../深入Kotlin.md) \ No newline at end of file +[下一篇:深入 Kotlin](./深入Kotlin.md) + +[进入 KMP](./进入KMP.md) \ No newline at end of file diff --git a/Learning/引入Kotlin.md b/Learning/引入Kotlin.md index 3eee402..3a7b03b 100644 --- a/Learning/引入Kotlin.md +++ b/Learning/引入Kotlin.md @@ -910,10 +910,12 @@ val copied = user.copy(name = "Bob") println(copied) // User(id=1, name=Bob) ``` -`data class` 会自动生成 `toString`、`equals`、`hashCode`、`copy` 等方法,很适合表示简单数据。 +`data class` 会自动生成 `toString`、`equals`、`hashCode`、`copy` 等方法,很适合表示简单数据。是一个替代 `Lombook` 的好东西。 ## 接下来可以看 [目录](../README.md) -[下一篇:深入 Kotlin](../深入Kotlin.md) \ No newline at end of file +[下一篇:深入 Kotlin](./深入Kotlin.md) + +[进入 KMP](./进入KMP.md) \ No newline at end of file diff --git a/Learning/深入Kotlin.md b/Learning/深入Kotlin.md new file mode 100644 index 0000000..3853b21 --- /dev/null +++ b/Learning/深入Kotlin.md @@ -0,0 +1,10 @@ +# 深入 Kotlin + + +## 接下来可以看 + +[目录](../README.md) + +[下一篇:Kotlin Springboot](./Kotlin-Springboot.md) + +[进入 KMP](./进入KMP.md) \ No newline at end of file diff --git a/Learning/第一个KMP应用.md b/Learning/第一个KMP应用.md new file mode 100644 index 0000000..9924db1 --- /dev/null +++ b/Learning/第一个KMP应用.md @@ -0,0 +1 @@ +# 第一个KMP应用 \ No newline at end of file diff --git a/Learning/进入KMP.md b/Learning/进入KMP.md index 1b4a850..b6e84e5 100644 --- a/Learning/进入KMP.md +++ b/Learning/进入KMP.md @@ -1,8 +1,144 @@ -# 进入KMP +# 进入 KMP > Kotlin Multiplatform 是基于 Jetpack Compose 框架的多平台应用编译技术,目前支持 Android,所有 JVM 平台,IOS,JS 网页(已被抛弃),WASM 网页。 ## 选择一个 IDE 这可能感觉上很怪,但是 **Android Stuidio 对KMP的支持确实是最好的**。 -如果你不想做 Android 的部分,用 IDEA 也影响不大,而且 IDEA 有官方中文,Android Stuidio 没有。 \ No newline at end of file +如果你不想做 Android 的部分,用 IDEA 也影响不大,而且 IDEA 有官方中文,而 Android Stuidio 没有。 + +## 项目的基本结构 + +一般来讲,一个 KMP 项目在生成的时候,长这样 + +- 项目根目录/ + - androidApp/ + - desktopApp/ + - iosApp/ + - webApp/ (JS 已经死了,现在向我们走来的是 WASM) + - shared/ + - src/ + - commonMain/ + - commonTest/ + - androidMain/ + - ...... + - build.gradle.kts + - .gitignore + - README.md + - gradlew (二进制脚本,一个bat,一个sh) + - gradle.proporties + - local.proporties (默认被 ignore) + - build.gradle.kts + - settings.gradle.kts + +如果你的项目还要包含 server 的部分的话,那会稍微复杂一点点。 + +### 各部分用途 + +所有的共享的部分(如UI,基础逻辑)都写在 `shared/commonMain` 里面,不同平台如果要做不同的效果,那么,相应的代码写在对应的 `Main` 里面,`App` 目录主要控制打包设置和进入软件的窗口设定,虽然可以写针对不同平台的不同效果,但是不推荐。 + +### 控制依赖与版本 + +- `build.gradle.kts` 不要动他就好了 +- `settings.gradle.kts` 可以设置 Maven 镜像之类 +- `shared/build.gradle.kts` 用于控制各个部分会用到的依赖和基本编译选项 +- `androidApp/build.gradle.kts` 用于控制 Android 应用编译时的选项 +- `desktopApp/build.gradle.kts` 用于控制 desktop 应用编译时的选项 +- ...... + +## 基本的 UI 结构 + +KMP 的 UI 以 `@Composable` 函数为核心,各个平台的 `main()` 函数入口都会默认调用 `shared/commonMain/kotlin` 中的 `App()` 函数。基本的 UI 也都会在 commonMain 中编写。 + +### @Composable 函数 + +我们先讲 `@Composable`,它是一个注解,用于函数或 lambda 变量: + +```kotlin +@Composable +fun MyButton(a: Int) { + TODO() // Composable 函数推荐大写首字母 +} + +val myCard = @Composable { a: Int -> + TODO() // lambda 不推荐 +} // 变量类型是 @Composable (Int) -> Unit +``` + +它有几个重要的特点: + +- Composable 函数只能在 main 函数或 Composable 函数中被调用。 +- 当 Composable 函数的参数发生变化时,它会被立刻重组(重新触发),受影响的 UI 立刻重新绘制。 +- 如果 Composable 函数的 lambda 参数的函数体中出现了 `Compose state` 对象,那么,当它的状态变化时,函数也会被重组。 + +### 保护变量值 + +当 Composable 函数被重新触发时,函数中定义的变量值也会重新被定义,这是很不好的,尤其是当我们需要保留变量值的时候。这里,我们有两种方法: + +1. 使用 remember 定义变量,使其变成状态对象。函数重组时,新的变量会保留重组前的值。 + +```kotlin +@Composable +def Counter() { + // 每次重组时,randomId 不会被重新生成,始终保留第一次的值 + val randomId = remember { UUID.randomUUID().toString() } + // 如果是可变变量,常使用委托,这里,它是一个枚举类 + var currentScreen by remember { mutableStateOf(Screens.Home) } +} + +@Composable +def UserProfile(userId: String) { + // 只有当 userId 发生变化时,remember 才会重新计算并更新 userCache + val userCache = remember(userId) { + fetchUserCacheFromDb(userId) + } +} +``` + +2. 在上一层函数中定义变量,然后把`变量`和`用于修改变量的 lambda 变量`一并传进下一个函数。 + +```kotlin +// 1. 父级函数:持有状态并定义修改逻辑 +@Composable +fun ParentScreen() { + var count by remember { mutableStateOf(0) } + + CounterDisplay( + count = count, // 将变量和对应的 Lambda 传入子函数 + onIncrement = { count++ } // 用于修改变量的 lambda 变量定义于此 + ) +} + +// 2. 子级函数:无状态组件(Stateless) +@Composable +fun CounterDisplay( + count: Int, // 传入变量值 + onIncrement: () -> Unit // 配套的 Lambda +) { + Button(onClick = onIncrement) { + Text("当前点击次数: $count") + } +} +``` + +### 引发重组 + +一般来讲就是利用改变参数的值和改变 lambda 中的 Compose state 状态来触发。利用参数变化并没有很常见。 + +```kotlin +@Composable +def Counter() { + // 使用 remember + mutableStateOf 保持状态并在值变化时触发重组 + var count by remember { mutableStateOf(0) } + + Button(onClick = { count++ }) { + Text("点击次数: $count") + } +} +``` + +## 接下来可以看 + +[目录](../README.md) + +[下一篇:第一个 KMP 应用](./第一个KMP应用.md) \ No newline at end of file diff --git a/README.md b/README.md index 7dc295b..7dba6ad 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,14 @@ ### 引入 Kotlin 1. [引入 Kotlin](Learning/引入Kotlin.md) -2. +2. [深入 Kotlin](Learning/深入Kotlin.md) +3. [Kotlin Springboot](Learning/Kotlin-Springboot.md) +4. ### Kotlin Multiplatform -1. [基本的项目结构](Learning/基本的项目结构.md) -2. [基本的 UI 结构](Learning/基本的UI结构.md) +1. [进入 KMP](learning/进入KMP.md) +2. [第一个 KMP 应用](Learning/第一个KMP应用.md) 3. ### 番外 @@ -20,6 +22,4 @@ ## 有关 KMP 的项目结构 -KMP的项目结构一直在变化,但好在公用代码的目录一直是`commonMain`,如果没有特别的说明,那么我就是在说`shared/src/commonMain`的事。 - - \ No newline at end of file +KMP 的项目结构一直在变化,但好在公用代码的目录一直是 `commonMain`,如果没有特别的说明,那么我就是在说 `shared/src/commonMain` 的事。