Slide 1

Slide 1 text

Jetpack Composeで 様々な画面サイズに対応する 株式会社ZOZO
 ブランドソリューション開発本部 FAANS部 フロントエンドブロック
 
 田中 崇裕 Copyright © ZOZO, Inc. 1

Slide 2

Slide 2 text

© ZOZO, Inc. 株式会社ZOZO ブランドソリューション開発本部 FAANS部 フロントエンドブロック 田中 崇裕 ● 2023年10月中途入社 ● 3年日記をもう少しで書き終えます 2

Slide 3

Slide 3 text

© ZOZO, Inc. 3 目次 1. 様々な画面サイズに対応することについて 2. WindowSizeClassを用いた実装 3. その他の手法 4. まとめ

Slide 4

Slide 4 text

© ZOZO, Inc. 4 1 様々な画面サイズに対応することについて 実情 ● なかなか優先度が高くない ● 色々な形や大きさの端末があって気にしていたらキリがない

Slide 5

Slide 5 text

© ZOZO, Inc. 5 1 様々な画面サイズに対応することについて 実情 ● なかなか優先度が高くない ● 色々な形や大きさの端末があって気にしていたらキリがない しかし ● Androidプラットフォームは様々な端末にアプローチしやすい ● 市場にモバイル以外の端末が増えてきている

Slide 6

Slide 6 text

© ZOZO, Inc. 6 1 様々な画面サイズに対応することについて 実情 ● なかなか優先度が高くない ● 色々な形や大きさの端末があって気にしていたらキリがない しかし ● Androidプラットフォームは様々な端末にアプローチしやすい ● 市場にモバイル以外の端末が増えてきている 対応することについて ● UX向上に少なからず繋がるはず ● いま対応するかはさておき、いずれ対応が必要になった時に心理的ハードルが低く なっていることが大事

Slide 7

Slide 7 text

© ZOZO, Inc. 7 2 WindowSizeClassを用いた実装 2-1 対応する画面サイズの定義 2-2 事前準備 2-3 実装例 (ナビゲーション、ペイン)

Slide 8

Slide 8 text

© ZOZO, Inc. 8 2-1 対応する画面サイズの定義 マテリアルデザインで分類されている3つのサイズに対して対応する (heightにも同じ定義があるが今回はwidthを使用) https://codelabs.developers.google.com/jetpack-compose-adaptability?hl=en#2

Slide 9

Slide 9 text

© ZOZO, Inc. 9 2-1 対応する画面サイズの定義 マテリアルデザインで分類されている3つのサイズに対して対応する (heightにも同じ定義があるが今回はwidthを使用) →WindowSizeClassを用いれば簡単に分岐が可能 https://codelabs.developers.google.com/jetpack-compose-adaptability?hl=en#2

Slide 10

Slide 10 text

© ZOZO, Inc. 10 2-2 事前準備 依存関係を追加 dependencies { implementation "androidx.compose.material3:material3-window-size-class:1.1.0" }

Slide 11

Slide 11 text

© ZOZO, Inc. 11 2-2 事前準備 端末の横幅を取得する @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ReplyTheme { val windowSize = calculateWindowSizeClass(this) ReplyApp(windowSize.widthSizeClass) } } }

Slide 12

Slide 12 text

© ZOZO, Inc. 12 2-2 事前準備(補足) @Previewで別々の端末サイズを指定可能 @Preview(showBackground = true, widthDp = 700) @Composable fun ReplyAppPreviewTablet() { ReplyTheme { ReplyApp( replyHomeUIState = ReplyHomeUIState(,,,), windowSize = WindowWidthSizeClass.Medium ) } }

Slide 13

Slide 13 text

© ZOZO, Inc. 13 2-2 事前準備(補足) AndroidStudio>Tools>DeviceManager>CreateDvices>Resizable(Experimental)をAPI33で作成

Slide 14

Slide 14 text

© ZOZO, Inc. 14 2-3 実装例 端末サイズによって以下の出し分けを実施する ● ナビゲーション ● ペイン

Slide 15

Slide 15 text

© ZOZO, Inc. 15 2-3 実装例(ナビゲーション) ナビゲーションの種類 ● Bottom Navigation ● Navigation Rail ● NavigationDrawer ○ Modal Navigation Drawer ○ Permanent Navigation Drawer

Slide 16

Slide 16 text

© ZOZO, Inc. 16 2-3 実装例(ナビゲーション) ナビゲーションの種類 ● Bottom Navigation ● Navigation Rail ● NavigationDrawer ○ Modal Navigation Drawer ○ Permanent Navigation Drawer

Slide 17

Slide 17 text

© ZOZO, Inc. 17 2-3 実装例(ナビゲーション) ナビゲーションの種類 ● Bottom Navigation ● Navigation Rail ● NavigationDrawer ○ Modal Navigation Drawer ○ Permanent Navigation Drawer

Slide 18

Slide 18 text

© ZOZO, Inc. 18 2-3 実装例(ナビゲーション) ナビゲーションの種類 ● Bottom Navigation ● Navigation Rail ● NavigationDrawer ○ Modal Navigation Drawer ○ Permanent Navigation Drawer enum class ReplyNavigationType { BOTTOM_NAVIGATION, NAVIGATION_RAIL, PERMANENT_NAVIGATION_DRAWER }

Slide 19

Slide 19 text

© ZOZO, Inc. 19 2-3 実装例(ナビゲーション) @Composable fun ReplyApp( windowSize: WindowWidthSizeClass, ) { val navigationType = when (windowSize) { WindowWidthSizeClass.Compact -> ReplyNavigationType.BOTTOM_NAVIGATION WindowWidthSizeClass.Medium -> ReplyNavigationType.NAVIGATION_RAIL WindowWidthSizeClass.Expanded -> ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER else -> ReplyNavigationType.BOTTOM_NAVIGATION } ReplyNavigationWrapperUI(navigationType) }

Slide 20

Slide 20 text

© ZOZO, Inc. 20 2-3 実装例(ナビゲーション) @Composable private fun ReplyNavigationWrapperUI( navigationType: ReplyNavigationType, ) { if (navigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER) { PermanentNavigationDrawer( drawerContent = { PermanentDrawerSheet { NavigationDrawerContent() //これは自分で作成 } } ) { ReplyAppContent(navigationType) //さらに子に渡す } }

Slide 21

Slide 21 text

© ZOZO, Inc. 21 2-3 実装例(ナビゲーション) } else { ModalNavigationDrawer( drawerContent = { ModalDrawerSheet { NavigationDrawerContent() //これは自分で作成 } }, ) { ReplyAppContent(navigationType) //さらに子に渡す } }

Slide 22

Slide 22 text

© ZOZO, Inc. 22 2-3 実装例(ナビゲーション) @Composable fun ReplyAppContent( navigationType: ReplyNavigationType, ) { Row(,,,) { AnimatedVisibility(visible = navigationType == ReplyNavigationType.NAVIGATION_RAIL) { ReplyNavigationRail() //これは自分で作成 } Column(,,,) { ReplyListOnlyContent() AnimatedVisibility(visible = navigationType == ReplyNavigationType.BOTTOM_NAVIGATION) { ReplyBottomNavigationBar() //これは自分で作成 } } } }

Slide 23

Slide 23 text

© ZOZO, Inc. 23 2-3 実装例(ペイン) ペインとは → 区画のこと 1つ 2つ

Slide 24

Slide 24 text

© ZOZO, Inc. enum class ReplyContentType { LIST_ONLY, LIST_AND_DETAIL } 24 2-3 実装例(ペイン) ペインとは → 区画のこと LIST_ONLY LIST_AND_DETAIL

Slide 25

Slide 25 text

© ZOZO, Inc. 25 2-3 実装例(ペイン) @Composable fun ReplyApp( windowSize: WindowWidthSizeClass, ) { val contentType = when (windowSize) { WindowWidthSizeClass.Compact -> ReplyContentType.LIST_ONLY WindowWidthSizeClass.Medium -> ReplyContentType.LIST_ONLY WindowWidthSizeClass.Expanded -> ReplyContentType.LIST_AND_DETAIL else -> ReplyContentType.LIST_ONLY } ReplyNavigationWrapperUI(contentType) // ReplyContent()に渡すのみ }

Slide 26

Slide 26 text

© ZOZO, Inc. 26 2-3 実装例(ペイン) @Composable fun ReplyAppContent( contentType: ReplyContentType, ) { Row(,,,) { Column(,,,) { if (contentType == ReplyContentType.LIST_AND_DETAIL) { ReplyListAndDetailContent() //これは自分で作成 } else { ReplyListOnlyContent() //これは自分で作成 } } } }

Slide 27

Slide 27 text

© ZOZO, Inc. 27 3 その他の手法 3-1 ListDetailPaneScaffold 3-2 NavigationSuiteScaffold ※どちらもalpha版 https://medium.com/androiddevelopers/new-apis-for-adaptive-layouts-in-jetpack-compose-f27cace48bcd

Slide 28

Slide 28 text

© ZOZO, Inc. 28 3-1 ListDetailPaneScaffold ListDetailPaneScaffold( ,,, listPane = { MyList(,,,) }, detailPane = { MyDetails(,,,) }, ) https://medium.com/androiddevelopers/new-apis-for-adaptive-layouts-in-jetpack-compose-f27cace48bcd implementation "androidx.compose.material3:material3-adaptive-android:1.0.0-alpha01"

Slide 29

Slide 29 text

© ZOZO, Inc. 29 3-2 NavigationSuiteScaffold NavigationSuiteScaffold( navigationSuiteItems = { navItems.forEachIndexed {,,, -> item( icon = {,,,}, label = {,,,}, selected = ,,,, onClick = {,,,} ) } } ) { // Screen content. Text(,,,) } https://medium.com/androiddevelopers/new-apis-for-adaptive-layouts-in-jetpack-compose-f27cace48bcd implementation "androidx.compose.material3:material3-adaptive-navigation-suite-android:1.0.0-alpha01"

Slide 30

Slide 30 text

© ZOZO, Inc. 30 4 まとめ ● 様々な画面サイズに対応する場合、対応する形を最初に決める ● 画面サイズ対応する際はWindowSizeClassのコールバックを活用する ● 取得したサイズごとにナビゲーションやペインを出し分けることで対応 ● 今後、より実装しやすくなることに期待

Slide 31

Slide 31 text

No content