Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Composeでカスタムレイアウトを組むときの気持ち

mikan
June 21, 2023

 Composeでカスタムレイアウトを組むときの気持ち

YUMEMI.grow Mobile #4の発表資料

mikan

June 21, 2023
Tweet

More Decks by mikan

Other Decks in Technology

Transcript

  1. 自己紹介 object Mikan { val name = " 一瀬喜弘" val

    company = "karabiner.tech" val hoby = listOf( " 漫画", " アニメ", " ゲーム", " 折り紙", "OSS 開発・コントリビュート", ) }
  2. 今日発表すること 🙏 Google I/O, WWDC に関連しない 📐 Layout API ❌

    measurable, placeable, constraints ⭕ UI 要素のポジションを決める計算をするときの思考 ↓ ソースコード
  3. インターフェース CenterAlignedTopAppBar を参考にします @Composable fun MyCenterAlignedTopAppBar( title: @Composable () ->

    Unit, modifier: Modifier = Modifier, navigationIcon: @Composable () -> Unit = {}, actions: @Composable () -> Unit = {} ): Unit
  4. Layout Layout( modifier = modifier, content = { Box( modifier

    = Modifier.layoutId("navigationIcon"), ) { navigationIcon() } Box(modifier = Modifier.layoutId("title")) { title() } Box( modifier = Modifier.layoutId("actions"), ) { actions() } }, ) { measurables, constraints -> // ...
  5. つづき ) { measurables, constraints -> val navigationIconPlaceable = measurables.first

    { it.layoutId == "navigationIcon" } .measure(constraints.copy(minWidth = 0)) val actionsPlaceable = measurables.first { it.layoutId == "actions" } .measure(constraints.copy(minWidth = 0)) // タイトルの横幅がアイコンを侵略しないようにする val maxTitleWidth = (constraints.maxWidth - navigationIconPlaceable.width - actionsPlaceable.width) .coerceAtLeast(0) // 0 未満にならないようにガードする val titlePlaceable = measurables.first { it.layoutId == "title" } .measure(constraints.copy(minWidth = 0, maxWidth = maxTitleWidth)) // ...
  6. つづき val maxTitleWidth = (constraints.maxWidth - navigationIconPlaceable.width - actionsPlaceable.width) .coerceAtLeast(0)

    // 0 未満にならないようにガードする ) { measurables, constraints -> val navigationIconPlaceable = measurables.first { it.layoutId == "navigationIcon" } .measure(constraints.copy(minWidth = 0)) val actionsPlaceable = measurables.first { it.layoutId == "actions" } .measure(constraints.copy(minWidth = 0)) // タイトルの横幅がアイコンを侵略しないようにする val titlePlaceable = measurables.first { it.layoutId == "title" } .measure(constraints.copy(minWidth = 0, maxWidth = maxTitleWidth)) // ...
  7. つづき // CenterAlignedTopAppBar はいい感じに高さを計算していたが、面倒なので一旦固定 val height = 64.dp.roundToPx() layout(constraints.maxWidth, height)

    { // 左寄せ navigationIconPlaceable.placeRelative( x = 0, y = (height - navigationIconPlaceable.height) / 2 ) // 中央寄せ titlePlaceable.placeRelative( x = (constraints.maxWidth - titlePlaceable.width) / 2, y = (height - titlePlaceable.height) / 2 ) // 右寄せ actionsPlaceable.placeRelative( x = (constraints.maxWidth - actionsPlaceable.width), y = (height - actionsPlaceable.height) / 2 ) } )
  8. placeable によるレイアウト : ナビゲーションアイコン y x (0, 0) (?, ?)

    navigationIconPlaceable.placeRelative( x = 0, // 左詰め y = (height - navigationIconPlaceable.height) / 2 // 中央 )
  9. placeable によるレイアウト : ナビゲーションアイコン y x (0, 0) (0, ?)

    navigationIconPlaceable.placeRelative( x = 0, // 左詰め y = (height - navigationIconPlaceable.height) / 2 // 中央 )
  10. placeable によるレイアウト : ナビゲーションアイコン (0, 0) (0, (height - navigationIconPlaceable.height)

    / 2) navigationIconPlaceable.placeRelative( x = 0, // 左詰め y = (height - navigationIconPlaceable.height) / 2 // 中央 )
  11. placeable によるレイアウト : タイトル x = (constraints.maxWidth - titlePlaceable.width) /

    2 y = (height - titlePlaceable.height) / 2 titlePlaceable.placeRelative( x = (constraints.maxWidth - titlePlaceable.width) / 2, y = (height - titlePlaceable.height) / 2 )
  12. placeable によるレイアウト : アクション x = (constraints.maxWidth - actionsPlaceable.width) y

    = (height - actionsPlaceable.height) / 2 actionsPlaceable.placeRelative( x = (constraints.maxWidth - actionsPlaceable.width), y = (height - actionsPlaceable.height) / 2 )
  13. 中くらいの長さにおける右寄せ、左寄せを考慮する // .. val titleX = if ((constraints.maxWidth / 2)

    < ((titlePlaceable.width / 2) + navigationIconPlaceable.width)) { // 左寄せ navigationIconPlaceable.width } else if ((constraints.maxWidth / 2) < ((titlePlaceable.width / 2) + actionsPlaceable.width)) { // 右寄せ constraints.maxWidth - actionsPlaceable.width - titlePlaceable.width } else { // 中央寄せ (constraints.maxWidth - titlePlaceable.width) / 2 } // .. layout(constraints.maxWidth, height) { // .. titlePlaceable.placeRelative( x = titleX, y = (height - titlePlaceable.height) / 2 ) // ... }
  14. 左寄せが必要なシチュエーション if ((constraints.maxWidth / 2) < ((titlePlaceable.width / 2) +

    navigationIconPlaceable.width)) { val titleX = navigationIconPlaceable.width // ...
  15. 右寄せが必要なシチュエーション } else if ((constraints.maxWidth / 2) < ((titlePlaceable.width /

    2) + actionsPlaceable.width)) { val titleX = // ... constraints.maxWidth - actionsPlaceable.width - titlePlaceable.width // ...
  16. 右寄せが必要なシチュエーション constraints.maxWidth - actionsPlaceable.width - titlePlaceable.width val titleX = //

    ... } else if ((constraints.maxWidth / 2) < ((titlePlaceable.width / 2) + actionsPlaceable.width)) { // ...