Slide 1

Slide 1 text

Layout API @MoyuruAizawa

Slide 2

Slide 2 text

Moyuru Aizawa Software Engineer of Catlog, RABO. Previously at Azit, CyberAgent, and Eureka. Love Metal, Hardcore and EDM. MoyuruAizawa

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Layout APIͱ͸?

Slide 5

Slide 5 text

‣ Column/Row͕಺෦Ͱ࢖༻͍ͯ͠ΔAPI ‣ ContentͷϨΠΞ΢τΛࣗ༝ʹܾఆͰ͖Δ ‣ ViewGroup΍LayoutManagerʹࣅͨଘࡏ ‣ Column/RowͰ͸දݱͰ͖ͳ͍͕ConstraintLayout΍BoxΛۦ࢖͢Δ ͷ͸ͪΐͬͱ໘౗ɺͱ͍ͬͨͱ͖ʹศར ‣ ͦΜͳঢ়گ͸͋·Γͳ͍ Layout APIͱ͸?

Slide 6

Slide 6 text

@Composable inline fun Layout( content: @Composable @UiComposable () -> Unit, modifier: Modifier = Modifier, measurePolicy: MeasurePolicy ) Layout API

Slide 7

Slide 7 text

@Composable inline fun Layout( content: @Composable @UiComposable () -> Unit, modifier: Modifier = Modifier, measurePolicy: MeasurePolicy ) Layout API

Slide 8

Slide 8 text

‣ Layout APIͷཁͱͳΔInterface ‣ MeasurePolicyΛ࣮૷ͯ͠Layout APIʹ౉͢͜ͱͰɺࣗ༝ͳϨΠΞ΢ τΛ࣮ݱͰ͖Δ MeasurePolicy

Slide 9

Slide 9 text

interface MeasurePolicy { fun MeasureScope.measure( measurables: List, constraints: Constraints ): MeasureResult … } MeasurePolicy

Slide 10

Slide 10 text

SimpleͳRowͷMeasurePolicyΛ ࣮૷ͯ͠ΈΔ

Slide 11

Slide 11 text

class SimpleRowMeasurePolicy : MeasurePolicy { override fun MeasureScope.measure( measurables: List, constraints: Constraints ): MeasureResult { … } } SimpleRowMeasurePolicy

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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֤ͯ͠ݸͷαΠζΛܾఆ͢Δ

Slide 14

Slide 14 text

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ͷαΠζΛΈͯ਌ͷαΠζΛܭࢉ͢Δ

Slide 15

Slide 15 text

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͍ͯ͘͠

Slide 16

Slide 16 text

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͍ͯ͘͠

Slide 17

Slide 17 text

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͍ͯ͘͠

Slide 18

Slide 18 text

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ʹՃࢉ͍ͯ͘͠

Slide 19

Slide 19 text

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ऴྃ

Slide 20

Slide 20 text

Layout( measurePolicy = remember { SimpleRowMeasurePolicy() }, content = { … } ) ͋ͱ͸InstanceΛLayoutʹ౉ͤ͹OK

Slide 21

Slide 21 text

༡ΜͰΈΔ

Slide 22

Slide 22 text

class OverlapRowMeasurePolicy(private val overlapWidth: Dp) : MeasurePolicy { override fun MeasureScope.measure(measurables: List, 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 (࢖͍ಓ͋Γͦ͏!)

Slide 23

Slide 23 text

class CircleMeasurePolicy : MeasurePolicy { override fun MeasureScope.measure(measurables: List, 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 (Ұੜ࢖Θͳͦ͏)

Slide 24

Slide 24 text

‣ Layout APIΛ࢖͏ͱࢠΛࣗࡏʹ഑ஔͰ͖Δ ‣ େ఍ͷUI͸Jetpack Composeඪ४/Accompanist͕͋Ε͹ࣄ଍ΓΔͷ Ͱɺར༻ස౓͸গͳ͍ͱࢥ͏ ‣ ߈ΊͯΔUIΛఏҊ͞Εͨ࣌ͷͨΊʹ͓͍֮͑ͯͯଛ͸ͳ͍͔΋ ‣ ࣮૷؆୯͍͍͍͗͌͌͢ ·ͱΊ

Slide 25

Slide 25 text

Thank you