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 Layout API
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Moyuru Aizawa
February 17, 2023
Programming
710
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Jetpack Compose Layout API
Moyuru Aizawa
February 17, 2023
More Decks by Moyuru Aizawa
See All by Moyuru Aizawa
BLUETOOTH_SCAN and iBeacon
lvla
1
150
graphicsLayer
lvla
0
300
BluetoothDevice.getName()に裏切られた話
lvla
0
410
Jetpack Composeで画像クロップ機能を実装する
lvla
0
1.3k
Jetpack Compose drag gesture and pinch gesture
lvla
1
4.3k
BLEを使ったアプリを継続的に開発するために
lvla
0
1.1k
RecyclerView.ItemAnimator
lvla
1
390
RecycledViewPool
lvla
1
300
CameraX
lvla
2
2.6k
Other Decks in Programming
See All in Programming
Android CLI
fornewid
0
210
Lean は証明の正しさを確認するためだけのツールって思ってませんか?
inoueasei
1
150
ドリフトを絶対に許さない(?)CDK運用 / CDK Ops with Zero Tolerance for Drifts (?)
akihisaikeda
1
170
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
450
【やさしく解説 設計編・中級 #4】ルールの寿命と、システムの年輪
panda728
PRO
2
180
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
460
わからない話を追いかけたら、プログラミング言語を作る側にいた
ydah
3
470
PHP Application における Kubernetes 内 gRPC 通信
ganchiku
0
580
自動化したのに回らないテスト運用の壁ーAI時代の品質責任と生産性
mfunaki
0
130
the container ship “Apple Silicon”@WWDC26 Recap -Japan-\(region).swift
shingangan
0
110
進化を続けるGo toolsの現在地 / The Current State of Ever-Evolving Go Tools
hond0413
0
150
OpenSpecのproposalにbrainstormingを持たせてみた
tigertora7571
1
200
Featured
See All Featured
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
330
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
280
Designing Experiences People Love
moore
143
24k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
950
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
890
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
WENDY [Excerpt]
tessaabrams
11
39k
Agile that works and the tools we love
rasmusluckow
331
22k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
2.3k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
Transcript
Layout API @MoyuruAizawa
Moyuru Aizawa Software Engineer of Catlog, RABO. Previously at Azit,
CyberAgent, and Eureka. Love Metal, Hardcore and EDM. MoyuruAizawa
None
Layout APIͱ?
‣ Column/Row͕෦Ͱ༻͍ͯ͠ΔAPI ‣ ContentͷϨΠΞτΛࣗ༝ʹܾఆͰ͖Δ ‣ ViewGroupLayoutManagerʹࣅͨଘࡏ ‣ Column/RowͰදݱͰ͖ͳ͍͕ConstraintLayoutBoxΛۦ͢Δ ͷͪΐͬͱ໘ɺͱ͍ͬͨͱ͖ʹศར ‣
ͦΜͳঢ়گ͋·Γͳ͍ Layout APIͱ?
@Composable inline fun Layout( content: @Composable @UiComposable () -> Unit,
modifier: Modifier = Modifier, measurePolicy: MeasurePolicy ) Layout API
@Composable inline fun Layout( content: @Composable @UiComposable () -> Unit,
modifier: Modifier = Modifier, measurePolicy: MeasurePolicy ) Layout API
‣ Layout APIͷཁͱͳΔInterface ‣ MeasurePolicyΛ࣮ͯ͠Layout APIʹ͢͜ͱͰɺࣗ༝ͳϨΠΞ τΛ࣮ݱͰ͖Δ MeasurePolicy
interface MeasurePolicy { fun MeasureScope.measure( measurables: List<Measurable>, constraints: Constraints ):
MeasureResult … } MeasurePolicy
SimpleͳRowͷMeasurePolicyΛ ࣮ͯ͠ΈΔ
class SimpleRowMeasurePolicy : MeasurePolicy { override fun MeasureScope.measure( measurables: List<Measurable>,
constraints: Constraints ): MeasureResult { … } } SimpleRowMeasurePolicy
val placeables = measurables.map { measurable -> measurable.measure(constraints.copy(minWidth = 0,
minHeight = 0)) } val parentWidth = placeables.sumOf { it.width } .coerceAtMost(constraints.maxWidth) val parentHeight = placeables.maxOf { it.height } .coerceAtMost(constraints.maxHeight) return layout(parentWidth, parentHeight) { var offsetX = 0 for (placeable in placeables) { placeable.place(offsetX, 0) offsetX += placeable.width if (offsetX > parentWidth) break } } MeasurePolicy#measure
val placeables = measurables.map { measurable -> measurable.measure(constraints.copy(minWidth = 0,
minHeight = 0)) } val parentWidth = placeables.sumOf { it.width } .coerceAtMost(constraints.maxWidth) val parentHeight = placeables.maxOf { it.height } .coerceAtMost(constraints.maxHeight) return layout(parentWidth, parentHeight) { var offsetX = 0 for (placeable in placeables) { placeable.place(offsetX, 0) offsetX += placeable.width if (offsetX > parentWidth) break } } measurables(children)Λmeasure֤ͯ͠ݸͷαΠζΛܾఆ͢Δ
val placeables = measurables.map { measurable -> measurable.measure(constraints.copy(minWidth = 0,
minHeight = 0)) } val parentWidth = placeables.sumOf { it.width } .coerceAtMost(constraints.maxWidth) val parentHeight = placeables.maxOf { it.height } .coerceAtMost(constraints.maxHeight) return layout(parentWidth, parentHeight) { var offsetX = 0 for (placeable in placeables) { placeable.place(offsetX, 0) offsetX += placeable.width if (offsetX > parentWidth) break } } placeablesͷαΠζΛΈͯͷαΠζΛܭࢉ͢Δ
val placeables = measurables.map { measurable -> measurable.measure(constraints.copy(minWidth = 0,
minHeight = 0)) } val parentWidth = placeables.sumOf { it.width } .coerceAtMost(constraints.maxWidth) val parentHeight = placeables.maxOf { it.height } .coerceAtMost(constraints.maxHeight) return layout(parentWidth, parentHeight) { var offsetX = 0 for (placeable in placeables) { placeable.place(offsetX, 0) offsetX += placeable.width if (offsetX > parentWidth) break } } placeablesΛplace͍ͯ͘͠
val placeables = measurables.map { measurable -> measurable.measure(constraints.copy(minWidth = 0,
minHeight = 0)) } val parentWidth = placeables.sumOf { it.width } .coerceAtMost(constraints.maxWidth) val parentHeight = placeables.maxOf { it.height } .coerceAtMost(constraints.maxHeight) return layout(parentWidth, parentHeight) { var offsetX = 0 for (placeable in placeables) { placeable.place(offsetX, 0) offsetX += placeable.width if (offsetX > parentWidth) break } } placeablesΛplace͍ͯ͘͠
val placeables = measurables.map { measurable -> measurable.measure(constraints.copy(minWidth = 0,
minHeight = 0)) } val parentWidth = placeables.sumOf { it.width } .coerceAtMost(constraints.maxWidth) val parentHeight = placeables.maxOf { it.height } .coerceAtMost(constraints.maxHeight) return layout(parentWidth, parentHeight) { var offsetX = 0 for (placeable in placeables) { placeable.place(offsetX, 0) offsetX += placeable.width if (offsetX > parentWidth) break } } placeablesΛplace͍ͯ͘͠
val placeables = measurables.map { measurable -> measurable.measure(constraints.copy(minWidth = 0,
minHeight = 0)) } val parentWidth = placeables.sumOf { it.width } .coerceAtMost(constraints.maxWidth) val parentHeight = placeables.maxOf { it.height } .coerceAtMost(constraints.maxHeight) return layout(parentWidth, parentHeight) { var offsetX = 0 for (placeable in placeables) { placeable.place(offsetX, 0) offsetX += placeable.width if (offsetX > parentWidth) break } } ஔͨ͠placeableͷwidth͚ͩoffsetʹՃࢉ͍ͯ͘͠
val placeables = measurables.map { measurable -> measurable.measure(constraints.copy(minWidth = 0,
minHeight = 0)) } val parentWidth = placeables.sumOf { it.width } .coerceAtMost(constraints.maxWidth) val parentHeight = placeables.maxOf { it.height } .coerceAtMost(constraints.maxHeight) return layout(parentWidth, parentHeight) { var offsetX = 0 for (placeable in placeables) { placeable.place(offsetX, 0) offsetX += placeable.width if (offsetX > parentWidth) break } } ͷ֎ʹग़ͨΒplaceऴྃ
Layout( measurePolicy = remember { SimpleRowMeasurePolicy() }, content = {
… } ) ͋ͱInstanceΛLayoutʹͤOK
༡ΜͰΈΔ
class OverlapRowMeasurePolicy(private val overlapWidth: Dp) : MeasurePolicy { override fun
MeasureScope.measure(measurables: List<Measurable>, constraints: Constraints): MeasureResult { val childConstraint = constraints.copy(minWidth = 0, minHeight = 0) val placeables = measurables.map { measurable -> measurable.measure(childConstraint) } val width = placeables.sumOf { it.width }.coerceAtMost(constraints.maxWidth) val height = placeables.maxOf { it.height }.coerceAtMost(constraints.maxHeight) return layout(width, height) { var offsetX = 0 for (placeable in placeables) { placeable.placeRelative(offsetX, 0) offsetX += (placeable.width - overlapWidth.toPx().toInt()) if (offsetX > width) break } } } } Overlap (͍ಓ͋Γͦ͏!)
class CircleMeasurePolicy : MeasurePolicy { override fun MeasureScope.measure(measurables: List<Measurable>, constraints:
Constraints): MeasureResult { val childConstraint = constraints.copy(minWidth = 0, minHeight = 0) val placeables = measurables.map { measurable -> measurable.measure(childConstraint) } val maxChildWidth = placeables.maxOf { it.width } return layout(constraints.maxWidth, constraints.maxHeight) { val radian = Math.toRadians(90.0 / placeables.lastIndex) val radius = constraints.maxWidth - maxChildWidth for (i in placeables.indices) { val x = (cos(radian * i) * radius).toInt() val y = (sin(radian * i) * radius).toInt() placeables[i].placeRelative(x, y) } } } } Circle (ҰੜΘͳͦ͏)
‣ Layout APIΛ͏ͱࢠΛࣗࡏʹஔͰ͖Δ ‣ େͷUIJetpack Composeඪ४/Accompanist͕͋ΕࣄΓΔͷ Ͱɺར༻සগͳ͍ͱࢥ͏ ‣ ߈ΊͯΔUIΛఏҊ͞Εͨ࣌ͷͨΊʹ͓͍֮͑ͯͯଛͳ͍͔ ‣
࣮؆୯͍͍͍͗͌͌͢ ·ͱΊ
Thank you