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
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
kenken
August 24, 2019
Programming
3.3k
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
730
Jetpack Compose さわってみた / tried writing code with Jetpack Compose
tkhs0604
0
910
Other Decks in Programming
See All in Programming
AIで効率化できた業務・日常
ochtum
0
150
「なぜそう決めたのか」を残し続ける仕組み ― Notion AI カスタムエージェント × Slack連携による設計判断の自動記録 - NIKKEI Tech Talk #47
niftycorp
PRO
0
230
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
180
Honoでのサプライチェーン侵害対策 〜 3つのライブラリに学ぶ
yusukebe
7
1.4k
The ROI of Quarkus for Spring Boot Applications
hollycummins
0
140
Vite+ Unified Toolchain for the Web
naokihaba
0
340
トークンをケチるな、設計しろ:GitHub Copilotを賢く使うコンテキスト戦略
ochtum
0
170
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.7k
並列実装の現場、2ヶ月間実務でAIを使い倒したAIもPCも私も限界が近い
ming_ayami
0
130
Claspは野良GASの夢をみるか
takter00
0
210
はてなアカウント基盤 State of the Union
cockscomb
1
730
依存関係から依存物へ―Dependencyという言葉の歴史をひも解く
j_lee
0
140
Featured
See All Featured
For a Future-Friendly Web
brad_frost
183
10k
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
My Coaching Mixtape
mlcsv
0
150
WENDY [Excerpt]
tessaabrams
11
38k
Ruling the World: When Life Gets Gamed
codingconduct
0
260
Producing Creativity
orderedlist
PRO
348
40k
Automating Front-end Workflow
addyosmani
1370
210k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
240
Paper Plane (Part 1)
katiecoart
PRO
0
9.2k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.5k
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 /