Slide 1

Slide 1 text

©2024 RAKUS Co., Ltd. Build Adaptive Layout by Jetpack Compose Mobile勉強会 Wantedly × チームラボ × Sansan #13 2024/03/05 @akkiee76

Slide 2

Slide 2 text

● Akihiko Sato ● ライブコミュニケーションアプリ開発 ● 𝕏 : @akkiee76 ● Speaker Deck : @akkie76 自己紹介 1

Slide 3

Slide 3 text

今日のテーマ Jetpack Compose での Adaptive Layout 対応方法 2

Slide 4

Slide 4 text

Adaptive Layoutとは 様々な画面サイズ、向き、フォームファクタに応じて 変化するレスポンシブな UI デザイン。 3 https://developer.android.com/jetpack/compose/layouts/adaptive

Slide 5

Slide 5 text

Material3 Foundation Window Class Sizes は以下の 3 つに分類されるので、表示状態に合 わせて設計するのではなく、 Window Class Sizesに重点を置くべき。 4 1. Compact 2. Medium 3. Expanded

Slide 6

Slide 6 text

Material3 Foundation 5 Window class (width) Breakpoint (dp) Common devices Compact Width < 600 Phone in portrait Medium 600 ≤ width < 840 Tablet in portrait Foldable in portrait (unfolded) Expanded 840 ≤ width < 1200* Phone in landscape Tablet in landscape Foldable in landscape (unfolded) Desktop * Android doesn’t yet support additional window size classes at 1200dp+ https://m3.material.io/foundations/layout/applying-layout/window-size-classes

Slide 7

Slide 7 text

Material3 Foundation 各 Window Classes で注意すること  1. Compact では小さくて隠れてしまう UI の表現  2. スクリーン分割  3. Component の再配置   ・Cards   ・List   ・Feeds   ・Panes 6 https://m3.material.io/foundations/layout/applying-layout/window-size-classes

Slide 8

Slide 8 text

Adaptive Layout への対応方法 🛠 7

Slide 9

Slide 9 text

Adaptive Layout への対応方法 🛠 対応方法は主に以下の 2 パターン  1. Window Size Class に応じたレイアウトを設定する  2. Composable 自身をレスポンシブに変更する 8

Slide 10

Slide 10 text

Window Size Class に応じた レイアウトを設定する方法 🛠 9

Slide 11

Slide 11 text

Window Size Class に応じたレイアウト設定方法 👉 WindowSizeClass が利用可能になる 10 dependencies { implementation("androidx.compose.material3:material3-window-size-class:1.2.0") } build.gradle.kts に依存関係を設定すると、

Slide 12

Slide 12 text

Window Size Class に応じたレイアウト設定方法 11 class MainActivity : AppCompatActivity() { @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { val windowSizeClass = calculateWindowSizeClass(this) AdaptiveMainScreen(windowSizeClass = windowSizeClass) } } } @Composable fun AdaptiveMainScreen(windowSizeClass: WindowSizeClass) { when (windowSizeClass.widthSizeClass) { WindowWidthSizeClass.Compact -> CompactScreen() WindowWidthSizeClass.Medium -> MediumScreen() WindowWidthSizeClass.Expanded -> ExpandedScreen() } } WindowSizeClassを取得 WindowSizeClassに応じた Composableを定義

Slide 13

Slide 13 text

Window Size Class に応じたレイアウト設定方法 12 @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) @Composable fun AdaptiveScreen() { val activity = LocalContext.current as MainActivity val widthSizeClass = calculateWindowSizeClass(activity = activity).widthSizeClass when (windowSizeClass.widthSizeClass) { WindowWidthSizeClass.Compact -> CompactScreen() WindowWidthSizeClass.Medium -> MediumScreen() WindowWidthSizeClass.Expanded -> ExpandedScreen() } } Composable 内でも使用可能

Slide 14

Slide 14 text

Composable 自身を レスポンシブに変更する方法 🛠 13

Slide 15

Slide 15 text

Composable レスポンシブに変更する方法 14 @Composable fun Card(imageUrl: String, title: String, description: String) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description(description) } Image(imageUrl) } } } } BoxWithConstraints によりレスポンシブにレイアウトに対応可能

Slide 16

Slide 16 text

Box と BoxWithConstraints の違い 15 @Composable inline fun Box( modifier: Modifier = Modifier, contentAlignment: Alignment = Alignment.TopStart, propagateMinConstraints: Boolean = false, content: @Composable BoxScope.() -> Unit ) @Composable fun BoxWithConstraints( modifier: Modifier = Modifier, contentAlignment: Alignment = Alignment.TopStart, propagateMinConstraints: Boolean = false, content: @Composable @UiComposable BoxWithConstraintsScope.() -> Unit )

Slide 17

Slide 17 text

BoxScope と BoxWithConstraintsScope の違い 16 @LayoutScopeMarker @Immutable interface BoxScope { @Stable fun Modifier.align(alignment: Alignment): Modifier @Stable fun Modifier.matchParentSize(): Modifier } @Stable interface BoxWithConstraintsScope : BoxScope { val constraints: Constraints val minWidth: Dp val maxWidth: Dp val minHeight: Dp val maxHeight: Dp }

Slide 18

Slide 18 text

Adaptive Layout への対応方法を紹介しました。 ・シンプルな UI であるほどレイアウトのスペースに要注意 ⚠ ・すべての Window Class Size に対応するのは大変なので、  まずは Expanded とそれ以外から進めると安心 💪 ・BoxWidthConstraints で特定の Compoment に対応するでもOK 💪   まとめ 17

Slide 19

Slide 19 text

Thank you ! 18