Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Jetpack Compose ことはじめ / the beginning of Jetpac...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
kenken
August 24, 2019
Programming
3.4k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Jetpack Compose ことはじめ / the beginning of Jetpack Compose
kenken
August 24, 2019
More Decks by kenken
See All by kenken
Maintaining E2E Test Automation as We Transition from View to Compose
tkhs0604
0
2.6k
try! Swift macros
tkhs0604
1
750
Jetpack Compose さわってみた / tried writing code with Jetpack Compose
tkhs0604
0
920
Other Decks in Programming
See All in Programming
ルールを書いて終わらせないハーネスエンジニアリング
yug1224
4
1.8k
Android CLI
fornewid
0
170
地域 SRE コミュニティ最前線 - ホンマでっかSRE勉強会
tk3fftk
0
270
Foundation Models frameworkで画像分析
ryodeveloper
1
140
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
540
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
110
5分で問診!Composer セキュリティ健康診断
codmoninc
0
600
これからAgentCoreを触る方へトレンドはGatewayです
har1101
6
500
共通化で考えるべきは、実装より公開する型だった
codeegg
0
280
AI がコードを書く時代における新卒エンジニアの仕事風景 (2026) / New Graduate Engineers in the Era of AI Coding (2026)
sushichan044
0
240
才能?センス?知らん、 続けたもん勝ちだ。-- 結婚・出産・癌を越えてなお、私がプロダクトを創り続ける理由
16bitidol
2
910
琵琶湖の水は止められてもNet--HTTPのリトライは止められない / You might be able to stop the water flow of Lake Biwa but you can't stop Net::HTTP retries
luccafort
PRO
0
440
Featured
See All Featured
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.5k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
A Tale of Four Properties
chriscoyier
163
24k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
Game over? The fight for quality and originality in the time of robots
wayneb77
1
230
Reality Check: Gamification 10 Years Later
codingconduct
0
2.2k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
280
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
390
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
400
The Art of Programming - Codeland 2020
erikaheidi
57
14k
Transcript
Jetpack Compose ことはじめ @tkhs0604 #kotlinfest Kotlin Fest 2019 @ 東京コンファレンスセンター・品川
• 高橋 健太 ◦ kenken | @tkhs0604 ◦ https://tkhs0604.hatenablog.com •
Gunosy Inc. • SI営業→エンジニア(iOS/Android/Web)→Androidエンジニア • アカペラ ◦ 最近ORICON NEWSに少しだけ載りました 自己紹介
本セッションの目標 • Jetpack Composeのさわりを理解する 本日のアジェンダ • Jetpack Compose とは •
サンプルアプリ • コンポーネントの作成方法 ◦ @Composable ◦ @Model 本セッションの目標 & 本日のアジェンダ
Jetpack Compose とは
• Google I/O 2019 • Android Jetpack • Kotlin •
宣言的UI ◦ cf. Flutter、React Native、Vue.js、SwiftUI • Pre-alpha版 (2019/8/23時点) ◦ ⚠大きく変更が入る可能性があります Jetpack Compose とは
サンプルアプリ
ToDoアプリ • タスクをリスト形式で表示 • アイテムまたはチェックボックスを タップすると、対応するタスクの状態 (完了 or 未完了)が切り替わる
コンポーネントの作成方法
@Composable & @Model @Composable • 関数に付与することで、コンポーネントとして認識される @Model • クラスに付与することで、コンポーネント内で状態を扱うためのクラ スとして使用可能になる
(※このままではただのクラス) • オブジェクトを+state関数に渡してStateオブジェクトを得る • Stateオブジェクトの内容が更新されると、フレームワーク側でUIの 再描画が行われる
ToDoアプリ class SampleActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) setContent { ToDoApp() } } } • Activity.setContentView() のような役割 • この中でコンポーネントを組み立てると、 フレームワークが最適に描画処理を行う • UIはAndroidCraneView内のCanvasに描画 される
ToDoAppコンポーネント @Composable fun ToDoApp() { // 20個のサンプルデータ。 val tasks =
(1..20).map { Task(id = it, description = "Task No.$it", isCompleted = false) } MaterialTheme { VerticalScroller { Surface { Column { ToDoList(tasks = tasks) } } } } } data class Task( val id: Int, val description: String, val isCompleted: Boolean )
ToDoAppコンポーネント @Composable fun ToDoApp() { // 20個のサンプルデータ。 val tasks =
(1..20).map { Task(id = it, description = "Task No.$it", isCompleted = false) } MaterialTheme { VerticalScroller { Surface { Column { ToDoList(tasks = tasks) } } } } } • @Composableアノテーションを付与 した関数はコンポーネントと認識される • コンポーネントをネストする形で組み立てる ので、UIの階層構造がコードで表現される
ToDoAppコンポーネント @Composable fun ToDoApp() { // 20個のサンプルデータ。 val tasks =
(1..20).map { Task(id = it, description = "Task No.$it", isCompleted = false) } MaterialTheme { VerticalScroller { Surface { Column { ToDoList(tasks = tasks) } } } } } • テーマを設定するためのコンポーネント • Material Designのテーマが設定される (はず)
ToDoAppコンポーネント @Composable fun ToDoApp() { // 20個のサンプルデータ。 val tasks =
(1..20).map { Task(id = it, description = "Task No.$it", isCompleted = false) } MaterialTheme { VerticalScroller { Surface { Column { ToDoList(tasks = tasks) } } } } } • 子コンポーネントを垂直方向にスクロール可能 にするためのコンポーネント • これがないと基本的にスクロールできない (子コンポーネントがリスト系でも )
ToDoAppコンポーネント @Composable fun ToDoApp() { // 20個のサンプルデータ。 val tasks =
(1..20).map { Task(id = it, description = "Task No.$it", isCompleted = false) } MaterialTheme { VerticalScroller { Surface { Column { ToDoList(tasks = tasks) } } } } } • タップ時のRipple Effectを表現するための コンポーネント • これがないとチェックボックスなどの タッチフィードバックが表現できない (※何なら現状はクラッシュするw )
ToDoAppコンポーネント @Composable fun ToDoApp() { // 20個のサンプルデータ。 val tasks =
(1..20).map { Task(id = it, description = "Task No.$it", isCompleted = false) } MaterialTheme { VerticalScroller { Surface { Column { ToDoList(tasks = tasks) } } } } } • 子コンポーネントを垂直方向にリスト表示する ためのコンポーネント (cf. Row) • ここではToDoとDividerを交互にリスト表示 している @Composable fun ToDoList(tasks: List<Task>) { tasks.forEach { ToDo(it) Divider( color = Color.LightGray, height = 1.dp ) } }
ToDoコンポーネント @Composable fun ToDo(task: Task) { val state = +state
{ TaskModel(description = task.description, isCompleted = task.isCompleted) } Clickable(onClick = { state.value.isCompleted = !state.value.isCompleted }) { Padding(padding = EdgeInsets(all = 20.dp)) { Row(crossAxisAlignment = CrossAxisAlignment.Start) { Checkbox( checked = state.value.isCompleted, onCheckedChange = { state.value.isCompleted = it } ) WidthSpacer(width = 10.dp) Text( text = "${task.description} ${if (state.value.isCompleted) "(completed)" else ""}" ) } } } } @Model data class TaskModel( var description: String, var isCompleted: Boolean )
ToDoコンポーネント @Composable fun ToDo(task: Task) { val state = +state
{ TaskModel(description = task.description, isCompleted = task.isCompleted) } Clickable(onClick = { state.value.isCompleted = !state.value.isCompleted }) { Padding(padding = EdgeInsets(all = 20.dp)) { Row(crossAxisAlignment = CrossAxisAlignment.Start) { Checkbox( checked = state.value.isCompleted, onCheckedChange = { state.value.isCompleted = it } ) WidthSpacer(width = 10.dp) Text( text = "${task.description} ${if (state.value.isCompleted) "(completed)" else ""}" ) } } } } @Model data class TaskModel( var description: String, var isCompleted: Boolean ) • @Modelアノテーションを付与した TaskModelオブジェクトを+state関数に 渡してStateオブジェクトを得る
ToDoコンポーネント @Composable fun ToDo(task: Task) { val state = +state
{ TaskModel(description = task.description, isCompleted = task.isCompleted) } Clickable(onClick = { state.value.isCompleted = !state.value.isCompleted }) { Padding(padding = EdgeInsets(all = 20.dp)) { Row(crossAxisAlignment = CrossAxisAlignment.Start) { Checkbox( checked = state.value.isCompleted, onCheckedChange = { state.value.isCompleted = it } ) WidthSpacer(width = 10.dp) Text( text = "${task.description} ${if (state.value.isCompleted) "(completed)" else ""}" ) } } } }
ToDoコンポーネント @Composable fun ToDo(task: Task) { val state = +state
{ TaskModel(description = task.description, isCompleted = task.isCompleted) } Clickable(onClick = { state.value.isCompleted = !state.value.isCompleted }) { Padding(padding = EdgeInsets(all = 20.dp)) { Row(crossAxisAlignment = CrossAxisAlignment.Start) { Checkbox( checked = state.value.isCompleted, onCheckedChange = { state.value.isCompleted = it } ) WidthSpacer(width = 10.dp) Text( text = "${task.description} ${if (state.value.isCompleted) "(completed)" else ""}" ) } } } }
ToDoコンポーネント @Composable fun ToDo(task: Task) { val state = +state
{ TaskModel(description = task.description, isCompleted = task.isCompleted) } Clickable(onClick = { state.value.isCompleted = !state.value.isCompleted }) { Padding(padding = EdgeInsets(all = 20.dp)) { Row(crossAxisAlignment = CrossAxisAlignment.Start) { Checkbox( checked = state.value.isCompleted, onCheckedChange = { state.value.isCompleted = it } ) WidthSpacer(width = 10.dp) Text( text = "${task.description} ${if (state.value.isCompleted) "(completed)" else ""}" ) } } } }
ToDoコンポーネント @Composable fun ToDo(task: Task) { val state = +state
{ TaskModel(description = task.description, isCompleted = task.isCompleted) } Clickable(onClick = { state.value.isCompleted = !state.value.isCompleted }) { Padding(padding = EdgeInsets(all = 20.dp)) { Row(crossAxisAlignment = CrossAxisAlignment.Start) { Checkbox( checked = state.value.isCompleted, onCheckedChange = { state.value.isCompleted = it } ) WidthSpacer(width = 10.dp) Text( text = "${task.description} ${if (state.value.isCompleted) "(completed)" else ""}" ) } } } }
ToDoコンポーネント @Composable fun ToDo(task: Task) { val state = +state
{ TaskModel(description = task.description, isCompleted = task.isCompleted) } Clickable(onClick = { state.value.isCompleted = !state.value.isCompleted }) { Padding(padding = EdgeInsets(all = 20.dp)) { Row(crossAxisAlignment = CrossAxisAlignment.Start) { Checkbox( checked = state.value.isCompleted, onCheckedChange = { state.value.isCompleted = it } ) WidthSpacer(width = 10.dp) Text( text = "${task.description} ${if (state.value.isCompleted) "(completed)" else ""}" ) } } } }
ToDoコンポーネント @Composable fun ToDo(task: Task) { val state = +state
{ TaskModel(description = task.description, isCompleted = task.isCompleted) } Clickable(onClick = { state.value.isCompleted = !state.value.isCompleted }) { Padding(padding = EdgeInsets(all = 20.dp)) { Row(crossAxisAlignment = CrossAxisAlignment.Start) { Checkbox( checked = state.value.isCompleted, onCheckedChange = { state.value.isCompleted = it } ) WidthSpacer(width = 10.dp) Text( text = "${task.description} ${if (state.value.isCompleted) "(completed)" else ""}" ) } } } } • stateのプロパティを更新することにより、フレー ムワーク側でUIの再描画が行われる 状態をトグルする チェックボックスの状態変 更を反映する
Pros. • ソースコードからUIが直感的に理解できる • kt/javaファイル、layout.xml、attrs.xml、drawable.xmlに 別個に書いていた内容を集約できる Cons. • ネスト地獄 •
Paddingとかの設定までコンポーネントで行うのは個人的には つらい (例えば、Textのプロパティとして渡したい) 所感
まだPre-alpha版なので…
Thank you! \ Follow me on Twitter /