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
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
Everything will be SERVERLESS — 信じて運用した10年の経験値 / Everything Will be Serverless — Lessons Learned from 10 Years of Operational Experience
seike460
PRO
1
1.1k
Agentic Software Factoryに、すごく賢いIF文を。 / super smart IF statement into the Agentic Software Factory.
rkaga
8
2.2k
AI主導のプログラミング学習アプリを作ってみた
econanringo
0
180
S3 TablesのIcebergデータに、AIエージェントからアクセスしてみた / 20261016developersio2026joetsu-accessing-iceberg-in-s3tables-via-agent
kasacchiful
PRO
0
150
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.6k
残高管理から台帳サービスへの進化
artoy
1
200
【加筆修正版】Laravel のアプリケーションをどこにデプロイするか #phpcon_ehime
akase244
0
150
The Rails Doctrine Decade
koic
2
590
外から家のPCの電源入れたい!をPHPで解決する
kawashimo0
1
130
npm との違いを手がかりにGo の依存管理を理解したい
optim
0
450
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
750
Featured
See All Featured
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
3.4k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
How to Think Like a Performance Engineer
csswizardry
28
2.9k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
56
3.5k
Designing for Timeless Needs
cassininazir
1
530
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
2
6.3k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
2
2.9k
Code Reviewing Like a Champion
maltzj
529
40k
Deep Space Network (abreviated)
tonyrice
0
370
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.4k
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
570
Bash Introduction
62gerente
615
220k
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 /