Share
Embed
Transcript
47 slides
-
Button(onClick = { showContent = !showContent }) { Text("Click me!")
} AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent, enter = scaleIn(), exit = scaleOut()) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } -
自己紹介 長澤 太郎 @ngsw_taro Kotlin Fest 代表 / Ubie株式会社 自称Kotlinエバンジェリスト
著書・講演など多数 Kotlin技術アドバイザーなど数社 -
1. Kotlinの復習 ・Kotlinの登場 ・Android開発における採用 ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史 ・初期 ・MVP ・Flux
・MVVM ・Jetpack Compose 3. 最新のKotlin事情 ・Kotlin 2.0 ・Kotlin Multiplatform ・Compose Multiplatform ・コードを交えた実装例 ・Fleet ・Android, iOS -
1. Kotlinの復習 ・Kotlinの登場 ・Android開発における採用 ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史 ・初期 ・MVP ・Flux
・MVVM ・Jetpack Compose 3. 最新のKotlin事情 ・Kotlin 2.0 ・Kotlin Multiplatform ・Compose Multiplatform ・コードを交えた実装例 ・Fleet ・Android, iOS -
Kotlinの登場 • 2011年7月 JVM Language Summitにて、開発中であることが発表された • IntelliJ IDEAなどの統合開発環境を開発・提供するJetBrains社 •
当時のJava言語の抱える課題に対して、Better Javaとして登場 ◦ Java仮想マシン(Java Virtual Machine)の上で走る言語 • JVMだけでなく、JavaScriptへのトランスパイルも視野に入れていた • 2016年にバージョン1.0がリリース -
Android開発における採用 • KotlinはAndroid開発には最適な言語だった • 当時のAndroid Javaは、Java SE 7に相当する言語機能だった ◦ コレクション操作やOptional型:
代わりに、GuavaやLightweight Stream APIを使った ◦ ラムダ式: 代わりに、Retrolambdaを使った List<String> nameLengths = new ArrayList<>(); for (String name: names) { nameLengths.add(name.length()); } List<String> nameLengths = Stream.of(names) .map(name -> name.length()) .toList(); -
Android開発における採用 • Kotlinは、上記の問題をすべてクリアしていた • 2017年、Androidの開発言語としてKotlinが正式サポートされた • KotlinはAndroid開発には最適な言語だった • 当時のAndroid Javaは、Java
SE 7に相当する言語機能だった ◦ コレクション操作やOptional型: 代わりに、GuavaやLightweight Stream APIを使った ◦ ラムダ式: 代わりに、Retrolambdaを使った -
Kotlinの便利なところ • Better Javaとして過不足なし • やや独特なところはあるが、文法や思想に大きなジャンプはない • ラムダ式があり、それを活かしたコレクション操作がある • 静的型付け
+ 型推論 • null安全 • 拡張関数 • プロパティ -
ラムダ式とコレクション操作 Project Euler Problem 1「1000未満の3か5の倍数となる数の合計」 var sum: Int = 0
for (var i: Int = 0; i < 1000; i++) { if (i % 3 != 0) continue if (i % 5 != 0) continue sum += i } println(sum) val sum: Int = (0 until 1000) .filter { it % 3 == 0 || it % 5 == 0 } .sum() println(sum) ※擬似コードです Kotlinでは、このようなforループは提供されていません ラムダ式 -
静的型付けと型推論 • 静的型付け: コンパイル時に、データ型の整合性を確認する • 型推論: 文脈から式の型を推論することで、型の明示的な記述を省略できる val list: MutableList<Int>
= mutableListOf<Int>(1, 2) list.add(3) list.add("4") // 型の不一致によるコンパイルエラー val list = mutableListOf(1, 2) list.add(3) list.add("4") // 型の不一致によるコンパイルエラー -
null安全 • 式がnullである可能性が、あるかないかを厳格に区別する • 式がnullである可能性がある場合、メンバーの呼び出しが制限される val name: String = "Kotlin"
println(name.length) val color: String? = "Red" println(color.length) // コンパイルエラー if (color != null) { println(color.length) // OK } println(color?.length) // OK -
拡張関数 • 型を変更せずに、メソッドを追加しているように見せる機能 fun hello(name: String): String { return "Hello,
$name!" } fun main() { val message = hello("World") println(message) // Hello, World! } fun String.hello(): String { return "Hello, $this!" } fun main() { val message = "World".hello() println(message) // Hello, World! } -
プロパティ • オブジェクトのデータを表現するインタフェース • つまり、データの実体がメモリに格納されているなどの詳細は不問 • デフォルトでは、データの実体はメモリに格納される class Name(val value:
String) { val length: Int = value.length } fun main() { val name = Name("Kotlin") val len = name.length println(len) } class Name(val value: String) { val length: Int get() { return value.length() } } -
1. Kotlinの復習 ・Kotlinの登場 ・Android開発における採用 ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史 ・初期 ・MVP ・Flux
・MVVM ・Jetpack Compose 3. 最新のKotlin事情 ・Kotlin 2.0 ・Kotlin Multiplatform ・Compose Multiplatform ・コードを交えた実装例 ・Fleet ・Android, iOS -
1. Kotlinの復習 ・Kotlinの登場 ・Android開発における採用 ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史 ・初期 ・MVP ・Flux
・MVVM ・Jetpack Compose 3. 最新のKotlin事情 ・Kotlin 2.0 ・Kotlin Multiplatform ・Compose Multiplatform ・コードを交えた実装例 ・Fleet ・Android, iOS -
初期 class MainActivity: Activity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.main) val textView = findViewById(R.id.text_view) as? TextView val button = findViewById(R.id.button) as? Button button?.setOnClickListener { textView?.setText("Clicked") } } } -
MVP (Model, View, Presenter) Activityとか <<interface>> View <<interface>> Presenter <<interface>>
Model PresenterImpl ModelImpl • Model = 業務ロジック, View = 表示, Presenter = ModelとViewの紐付け • 結合が疎になるので、テスタビリティ、メンテナビリティが向上 矢印の先に、依存している(呼び出している) 矢印の先を実装している -
MVVM (Model, View, ViewModel) • ViewModel = UIロジック、Model呼び出し、Viewへの更新通知 • Android
Architecture Componentsにより実装が容易 View ViewModel Model データの流れ DataBinding / LiveData -
Jetpack Compose • Kotlinによる宣言型UIツールキット • 考え方: 現在の状態から、現在のUIへと変換をする @Composable fun Greeting(name:
String) { Text(text = "Hello $name!") } @Composable fun GreetingList(names: List<String>, modifier: Modifier) { Column(modifier = modifier) { names.forEach { name -> Greeting(name = name) } } } -
1. Kotlinの復習 ・Kotlinの登場 ・Android開発における採用 ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史 ・初期 ・MVP ・Flux
・MVVM ・Jetpack Compose 3. 最新のKotlin事情 ・Kotlin 2.0 ・Kotlin Multiplatform ・Compose Multiplatform ・コードを交えた実装例 ・Fleet ・Android, iOS -
1. Kotlinの復習 ・Kotlinの登場 ・Android開発における採用 ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史 ・初期 ・MVP ・Flux
・MVVM ・Jetpack Compose 3. 最新のKotlin事情 ・Kotlin 2.0 ・Kotlin Multiplatform ・Compose Multiplatform ・コードを交えた実装例 ・Fleet ・Android, iOS -
Kotlin 2.0 • K2コンパイラが安定版に。従来の2倍の速度 • 文法の変更は、なし • バージョン2.1以降で、文法の変更・言語機能の追加が予定されている ◦ コンテキストパラメータ
◦ when内のガード条件 ◦ インライン関数の中でのcontinue, breakをサポート ◦ 文字列テンプレートにおける $ のエスケープ -
Kotlin Multiplatform (KMP) • Kotlinコードが、複数のプラットフォーム向けの実行コードになる • 理想: 1つのKotlinコードが、どこでも動く • 現実
◦ ロジックなど、プラットフォームに依存しないコードは、各プラットフォームで共有可能 ◦ UIなど、プラットフォームに依存するコードは、プラットフォームごとに記述する • expect / actual -
Kotlin Multiplatform (KMP) expect fun getName(): String fun greet(): String
= "Hello, ${getName()}!" actual fun getName(): String = "JVM" fun main() { println(greet()) } actual fun getName(): String = "Android" class MainActivity: Activity() { override fun onCreate(state: Bundle?) { super.onCreate(state) Log.i("Greet", greet()) } } 共有コード commonMain JVM固有コード jvmMain Android固有コード androidMain -
Compose Multiplatform (CMP) • Jetpack Composeベースの宣言型UI構築フレームワーク • 対応プラットフォーム ◦ Android
◦ デスクトップ(Windows, MacOS, Linux -> JVM) ◦ iOS(ベータ版) ◦ Wasm(アルファ版) -
Kotlinに関係する最新ツール • Fleet ◦ JetBrains製 ◦ シンプルかつパワフルなコードエディタ ◦ https://www.jetbrains.com/ja-jp/fleet/ ここからダウンロード
• Amper ◦ JetBrains製 ビルドツール ◦ 設定をYAMLで記述する -
commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent
by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } } -
commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent
by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } } -
commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent
by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } } -
commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent
by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } } -
commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent
by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } } -
commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent
by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } } -
androidMain/kotlin/…/MainActivity.kt class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) setContent { App() } } } -
iosMain/kotlin/MainViewController.kt fun MainViewController() = ComposeUIViewController { App() } 上記コードは、Composeとの繋ぎ込みに過ぎず、iOSアプリそのものはSwiftで記述されている //
<project_root>/iosApp/iosApp/ContentView.swift struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { MainViewControllerKt.MainViewController() } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} } struct ContentView: View { var body: some View { ComposeView().ignoresSafeArea(.keyboard) } } -
共通コード class Greeting { private val platform = getPlatform() fun
greet(): String { return "Hello, ${platform.name}!" } } interface Platform { val name: String } expect fun getPlatform(): Platform commonMain/kotlin/Greeting.kt commonMain/kotlin/Platform.kt -
プラットフォーム固有のコード class AndroidPlatform : Platform { override val name: String
= "Android ${Build.VERSION.SDK_INT}" } actual fun getPlatform(): Platform = AndroidPlatform() androidMain/kotlin/Platform.andoird.kt class IOSPlatform: Platform { override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion } actual fun getPlatform(): Platform = IOSPlatform() iosMain/kotlin/Platform.ios.kt -
まとめ • Android開発は今やKotlinで記述する利点が大きい • その恩恵を特に受けているのが Jetpack Compose • KMPによって、KotlinコードだけでAndroid, iOS,
サーバーサイド, その他で 走るアプリを作れるし、コードの共有・分離が適切に行える • CMPによって、Android, iOS向けのUIを同時に構築できる ご清聴ありがとうございました 長澤 太郎 @ngsw_taro