Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
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.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.7k
try! Swift macros
tkhs0604
1
790
Jetpack Compose さわってみた / tried writing code with Jetpack Compose
tkhs0604
0
940
Other Decks in Programming
See All in Programming
golang-bob.pdf
iwatsukayura
0
130
AIに “答え“を書かせるのを やめてみた
___akari11
0
120
npm との違いを手がかりにGo の依存管理を理解したい
optim
0
570
Compiling a real-world CLI tool to native with Static Hermes
tmikov2023
0
120
Domain-Driven Transformation
hschwentner
2
2.3k
【加筆修正版】Laravel のアプリケーションをどこにデプロイするか #phpcon_ehime
akase244
0
150
月間100万人のサービス刷新で、 技術選定をHonoに振り切った話
yutobara
8
6.7k
Kotlinの15年とServer-Side Kotlin
n_takehata
1
150
Agents on Rails - Rails at Scale 2026
irinanazarova
0
560
気持ちぃ〜角丸 Squircle
ryu1013
0
310
S3 TablesのIcebergデータに、AIエージェントからアクセスしてみた / 20261016developersio2026joetsu-accessing-iceberg-in-s3tables-via-agent
kasacchiful
PRO
0
160
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.5k
Featured
See All Featured
Building Flexible Design Systems
yeseniaperezcruz
330
41k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3.1k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.7k
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
120
120k
Build your cross-platform service in a week with App Engine
jlugia
234
19k
How to build a perfect <img>
jonoalderson
1
6.1k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
500
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Google's AI Overviews - The New Search
badams
0
1.6k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
310
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 /