Slide 1

Slide 1 text

基礎から学ぶ大画面対応 (Learning Large-Screen Support from the Ground Up) 〜「Large screen differentiated」認定アプリの開発知見〜 (— Development Insights from a “Large screen differentiated”- Certified App —) DroidKaigi 2025 / tomoya0x00 1

Slide 2

Slide 2 text

あなたのアプリは大画面対応できていますか? (Is Your App Ready for Large Screens?) 2

Slide 3

Slide 3 text

「私のアプリは大画面に完璧に対応できている」とい う人は挙手をお願いします! (Please raise your hand if "My app is perfectly optimized for large screens!") 3

Slide 4

Slide 4 text

できていなくても大丈夫、このセッションで基礎から 大画面対応を学んでいきましょう! (Don't worry if you haven't - let's learn about large screen support from the basics in this session!) 4

Slide 5

Slide 5 text

自己紹介 (Self Introduction) tomoya0x00 - X, GitHub, Zenn U-NEXT Co., Ltd Research & Development 過去の登壇 (Past Presentations) DroidKaigi 2018, 2019 DevFest & Android Dev Summit Japan 2022 5

Slide 6

Slide 6 text

About U-NEXT App - 1/3 VOD + Book のアプリ (VOD + Book app) 2024 年に フル Compose でリニューアル (Renewed with full Compose in 2024) 6

Slide 7

Slide 7 text

About U-NEXT App - 2/3 Google Play Best Of Awards 2022 「ベストアプリ 2022」と「タブレット部門 アプリ カテゴリ 大賞」のダブル受賞 (Double Award: "Best App 2022" and "Tablet Category Grand Prize") 2024 「マルチデバイス部門 アプリカテゴリ 大賞」を受賞 ("Multi-Device Category Grand Prize" Award) 7

Slide 8

Slide 8 text

About U-NEXT App - 3/3 Large screen app quality 2022 Large screen optimized 認定 (Large screen optimized Certified) 2025 日本 VOD アプリ初の Large screen differentiated 認定 (First Japanese VOD app certified as Large screen differentiated) 8

Slide 9

Slide 9 text

目次 (Agenda) 1. 大画面対応の重要性 (Importance of Large Screen Support) 2. 大画面対応のサポートレベル (Large screen support levels) 3. View ベースアプリの大画面対応戦略 (Large Screen Strategy for View-based Apps) 4. 基本的な大画面対応 (Large screen ready) 5. 大画面向けに最適化 (Large screen optimized) 6. 更なる大画面対応 (Further Large Screen Support) 7. まとめ (Summary) 9

Slide 10

Slide 10 text

目次 (Agenda) 1. 大画面対応の重要性 (Importance of Large Screen Support) 2. 大画面対応のサポートレベル (Large screen support levels) 3. View ベースアプリの大画面対応戦略 (Large Screen Strategy for View-based Apps) 4. 基本的な大画面対応 (Large screen ready) 5. 大画面向けに最適化 (Large screen optimized) 6. 更なる大画面対応 (Further Large Screen Support) 7. まとめ (Summary) 10

Slide 11

Slide 11 text

大画面対応の重要性 (Importance of Large Screen Support) 11

Slide 12

Slide 12 text

動作の変更点:Android 16 以上をターゲットとするア プリ (Behavior changes: Apps targeting Android 16 or higher) 12

Slide 13

Slide 13 text

Ignore orientation, resizability, and aspect ratio restrictions 画面の最小幅が 600dp 以上であれば、下記の制限が無視される (On displays with smallest width >= 600dp, the below restrictions no longer apply:) orientation resizability aspect ratio https://developer.android.com/about/versions/16/behavior-changes-16#ignore- orientation 13

Slide 14

Slide 14 text

一時的な opt-out (Temporary Opt-out) ... https://developer.android.com/about/versions/16/behavior-changes-16#temporary-opt- out 14

Slide 15

Slide 15 text

Target API level 37 では opt-out 不可 (No Opt-out for Target API Level 37) 15

Slide 16

Slide 16 text

Android release in 2026: Changes described above will be the baseline experience for large screen devices (smallest screen width >600dp) for apps that target API level 37. Developers will not have an option to opt-out. https://android-developers.googleblog.com/2025/01/orientation-and-resizability- changes-in-android-16.html 16

Slide 17

Slide 17 text

なぜ、このような挙動変更が行われるのか? (Why Are These Behavior Changes Being Made?) 17

Slide 18

Slide 18 text

With Android apps now running on a variety of devices (such as phones, tablets, foldables, desktops, cars, and TVs) and windowing modes on large screens (such as split screen and desktop windowing), developers should build Android apps that adapt to any screen and window size, regardless of device orientation. Paradigms like restricting orientation and resizability are too restrictive in today's multidevice world. https://developer.android.com/about/versions/16/behavior-changes-16#adaptive-layouts 18

Slide 19

Slide 19 text

新たなデバイスの登場 (Emergence of New Devices) 19

Slide 20

Slide 20 text

Android XR デバイス (Android XR Devices) スマホと比べて 広大な表示領域 (Vast display area compared to smartphones) スマホだけを考慮したデザインでは、この広大な表示領域を活かせない (Smartphone-only designs cannot utilize this vast display area) 20

Slide 21

Slide 21 text

以前 (Before) 大画面対応は余力があれば追加でやること (Nice-to-have feature) 21

Slide 22

Slide 22 text

現在 (Now) 大画面対応は当たり前にやること (Must-have feature) 22

Slide 23

Slide 23 text

目次 (Agenda) 1. 大画面対応の重要性 (Importance of Large Screen Support) 2. 大画面対応のサポートレベル (Large screen support levels) 3. View ベースアプリの大画面対応戦略 (Large Screen Strategy for View-based Apps) 4. 基本的な大画面対応 (Large screen ready) 5. 大画面向けに最適化 (Large screen optimized) 6. 更なる大画面対応 (Further Large Screen Support) 7. まとめ (Summary) 23

Slide 24

Slide 24 text

大画面対応のサポートレベル (Large screen support levels) 24

Slide 25

Slide 25 text

TIER 3 (basic) — Large screen ready 最低限動く (Basic functionality) TIER 2 (better) — Large screen optimized 快適に使える (Comfortable experience) TIER 1 (best) — Large screen differentiated 大画面を活かした体験を提供 (Large screen-first experience) https://developer.android.com/docs/quality-guidelines/large-screen-app-quality 25

Slide 26

Slide 26 text

TIER 3 (basic) — Large screen ready 重要なタスクフローは完了可能 (Users can complete critical task flows) ユーザーエクスペリエンスは最適ではない (Less than optimal user experience) Configuration changes を適切に処理 (Handle configuration changes properly) 全画面で実行 (Full screen) レイアウトは理想的でない可能性あり (App layout might not be ideal) レターボックス表示なし (Not letterboxed) 外部入力デバイスの基本サポート (Basic support for external input devices) キーボード、マウス、トラックパッド、スタイラス (Keyboard, mouse, trackpad, and stylus) https://developer.android.com/docs/quality-guidelines/large-screen-app- quality#tier_3_basic_large_screen_ready 26

Slide 27

Slide 27 text

レターボックス表示 (Letterboxing) https://developer.android.com/guide/practices/device-compatibility-mode#letterboxing 27

Slide 28

Slide 28 text

外部入力デバイスの基本サポート (Basic support for external input devices) キーボード (Keyboard) 外部キーボードを使用したテキスト入力に対応 (Supports text input using external keyboard) 外部キーボードの接続・接続解除時にアプリの再起動不要 (No app relaunch required when external keyboard is connected or disconnected) 物理キーボードと仮想キーボードの自動切り替え (Automatic switching between physical and virtual keyboards) マウス・トラックパッド (Mouse, and trackpad) クリック・選択・スクロール (Click, Select, and Scroll) https://developer.android.com/docs/quality-guidelines/large-screen-app- quality#large_screen_ready 28

Slide 29

Slide 29 text

TIER 2 (better) — Large screen optimized すべての画面サイズ・デバイス設定でレイアウトを最適化 (Layout optimizations for all screen sizes and device configurations) マルチウインドウモードを含む (Including multi-window mode) 外部入力デバイスのサポートを拡張 (Enhanced support for external input devices) https://developer.android.com/docs/quality-guidelines/large-screen-app- quality#tier_2_better_large_screen_optimized 29

Slide 30

Slide 30 text

外部入力デバイスのサポートを拡張 - キーボード (Enhanced support for external input devices - Keyboard) Tab・矢印キーによるキーボードナビゲーション対応 (Tab and arrow key navigation support) 選択、コピー&ペースト、元に戻すなどのキーボードショートカット対応 (Keyboard shortcuts for select, cut, copy, paste, undo, redo) Space キーでメディア再生・一時停止 (Spacebar plays and pauses media) Enter キーで送信機能実行 (Enter key performs send function in communication apps) https://developer.android.com/docs/quality-guidelines/large-screen-app-quality? hl=ja#large_screen_optimized 30

Slide 31

Slide 31 text

外部入力デバイスのサポートを拡張 - マウス、トラックパッド (Enhanced support for external input devices - Mouse, Trackpad) UI 要素のホバー状態表示 (Hover states for actionable UI elements) 右クリックでオプションメニュー表示 (Right-click accesses options menus) マウスホイール(Ctrl 併用) ・トラックパッドのピンチでズーム (Mouse scroll wheel (with Ctrl) and trackpad pinch for zoom) https://developer.android.com/docs/quality-guidelines/large-screen-app-quality? hl=ja#large_screen_optimized 31

Slide 32

Slide 32 text

TIER 1 (best) — Large screen differentiated 大画面デバイス向けにデザインされたユーザーエクスペリエンスを提供 (Provides user experience designed for large screen devices) 該当する場合、追加機能のサポート (Where applicable, additional feature support) マルチタスク、フォルダブルデバイスの形状、ドラッグ&ドロップ、スタイラ ス入力 (Multitasking, Foldable postures, Drag and drop, Stylus input) https://developer.android.com/docs/quality-guidelines/large-screen-app- quality#tier_1_best_large_screen_differentiated 32

Slide 33

Slide 33 text

Large screen differentiated App が提供する機能の例 (Example Features of Large Screen Differentiated Apps) Picture-in-picture Multiple instances Tabletop posture Dual display Drag and drop Draw and write within the app using a stylus https://developer.android.com/docs/quality-guidelines/large-screen-app- quality#large_screen_differentiated 33

Slide 34

Slide 34 text

Android XR との関係性 (Relationship with Android XR) 34

Slide 35

Slide 35 text

Android XR app quality guidelines Android XR compatible mobile app Android XR compatible large screen app Android XR differentiated app https://developer.android.com/docs/quality-guidelines/android-xr 35

Slide 36

Slide 36 text

Large screen app quality TIER 1 or 2 == Android XR compatible large screen app 大画面対応のサポートレベル:TIER 2 Large screen optimized を達成すれば、Android XR への準備も(ある程度は)OK となる (Achieving TIER 2 Large screen optimized means your app is (mostly) ready for Android XR) https://developer.android.com/docs/quality-guidelines/android-xr#android-xr-compatible 36

Slide 37

Slide 37 text

重要な注意事項 (Important Notes) 本セッションでは、私個人が特に重要と感じた項目を中心に解説します (This session focuses on items I personally consider most important) 各 TIER の全ての要件を網羅しているわけではありません (Not covering all requirements for each TIER) 実際の認定には、公式ドキュメントで要件一覧を確認し、対応が必要です (For actual certification, check the full requirements list in Google's official documentation) 37

Slide 38

Slide 38 text

目次 (Agenda) 1. 大画面対応の重要性 (Importance of Large Screen Support) 2. 大画面対応のサポートレベル (Large screen support levels) 3. View ベースアプリの大画面対応戦略 (Large Screen Strategy for View-based Apps) 4. 基本的な大画面対応 (Large screen ready) 5. 大画面向けに最適化 (Large screen optimized) 6. 更なる大画面対応 (Further Large Screen Support) 7. まとめ (Summary) 38

Slide 39

Slide 39 text

View ベースアプリの大画面対応戦略 (Large Screen Strategy for View-based Apps) 39

Slide 40

Slide 40 text

View ベースアプリとは? (What are View-based Apps?) ほとんどの画面が View で構成されているアプリ (Apps mostly composed of View screens) 一部の画面は Compose に移行済みな場合もある (Some screens may already be migrated to Compose) 40

Slide 41

Slide 41 text

推奨アプローチ (Recommended Approach) 1. View ベースのまま、TIER 3 (basic) — Large screen ready 相当の大画面対応を行う (Implement TIER 3 large screen support while keeping View-based architecture) Android 16 での Behavior changes に対応する事を優先する (Prioritize addressing Android 16 behavior changes) Android 17 での opt-out 無効化まで、あまり時間は無い (Limited time until opt-out disabled in Android 17) 2. その後は 全画面の Compose 化に注力する (Then focus on migrating to Compose) 最終的には全画面を Compose 化 することを強く推奨 (We strongly recommend migrating all screens to Compose) 41

Slide 42

Slide 42 text

Compose 化が推奨される理由 (Why Compose is Recommended) 42

Slide 43

Slide 43 text

柔軟なレイアウト変更(Flexible Layout Changes) 画面サイズ・ウインドウサイズに応じたレイアウトの切り替え実現が容易 (Easy to implement layout switching based on screen and window size) 43

Slide 44

Slide 44 text

Navigation 3 Navigation 3 で大画面に適したレイアウトを容易に実現 (Easy implementation of large screen layouts with Navigation 3) List-Detail 表示などを容易に実現 (Easy List-Detail implementation) Compose 専用ライブラリ (Navigation 3 is a Compose-only library) 44

Slide 45

Slide 45 text

目次 (Agenda) 1. 大画面対応の重要性 (Importance of Large Screen Support) 2. 大画面対応のサポートレベル (Large screen support levels) 3. View ベースアプリの大画面対応戦略 (Large Screen Strategy for View-based Apps) 4. 基本的な大画面対応 (Large screen ready) 5. 大画面向けに最適化 (Large screen optimized) 6. 更なる大画面対応 (Further Large Screen Support) 7. まとめ (Summary) 45

Slide 46

Slide 46 text

基本的な大画面対応 (Large screen ready) 46

Slide 47

Slide 47 text

TIER 3 (basic) — Large screen ready 重要なタスクフローは完了可能 (Users can complete critical task flows) ユーザーエクスペリエンスは最適ではない (Less than optimal user experience) Configuration changes を適切に処理 (Handle configuration changes properly) 全画面で実行 (Full screen) レイアウトは理想的でない可能性あり (App layout might not be ideal) レターボックス表示なし (Not letterboxed) 外部入力デバイスの基本サポート (Basic support for external input devices) キーボード、マウス、トラックパッド、スタイラス (Keyboard, mouse, trackpad, and stylus) https://developer.android.com/docs/quality-guidelines/large-screen-app- quality#tier_3_basic_large_screen_ready 47

Slide 48

Slide 48 text

「基本的な大画面対応」の進め方 (Steps for Basic Large Screen Support) 1. アプリを全画面で実行する (Run app in full screen) 2. Configuration changes を適切に処理する (Handle configuration changes properly) 3. 重要なタスクフローを完了できるようにする (Enable critical task flows) 48

Slide 49

Slide 49 text

「基本的な大画面対応」の進め方 (Steps for Basic Large Screen Support) 1. アプリを全画面で実行する (Run app in full screen) 2. Configuration changes を適切に処理する (Handle configuration changes properly) 3. 重要なタスクフローを完了できるようにする (Enable critical task flows) 49

Slide 50

Slide 50 text

アプリを全画面で実行する (Run App in Full Screen) 50

Slide 51

Slide 51 text

縦画面固定を解除する (Remove Portrait Orientation Lock) AndroidManifest.xml ↓ 51

Slide 52

Slide 52 text

アスペクト比の制限を解除する (Remove Aspect Ratio Restrictions) AndroidManifest.xml ↓ 52

Slide 53

Slide 53 text

リサイズ可能にする (Make App Resizable) AndroidManifest.xml ↓ 53

Slide 54

Slide 54 text

「基本的な大画面対応」の進め方 (Steps for Basic Large Screen Support) 1. アプリを全画面で実行する (Run app in full screen) 2. Configuration changes を適切に処理する (Handle configuration changes properly) 3. 重要なタスクフローを完了できるようにする (Enable critical task flows) 54

Slide 55

Slide 55 text

Configuration changes を適切に処理する (Handle Configuration Changes Properly) ※ Android アプリ開発初心者の方向けに、特に丁寧に説明します (Note: This section will be explained in detail for Android app development beginners) 55

Slide 56

Slide 56 text

Configuration changes とは? (What are Configuration Changes?) アプリの実行中に、デバイス構成の一部が変更される場合がある (Device configuration may change while your app is running) https://developer.android.com/guide/topics/resources/runtime-changes 56

Slide 57

Slide 57 text

変更される構成の例 (Examples of Configuration Changes) アプリの表示サイズ (Display size) 画面の向き (Screen orientation) フォントのサイズと太さ (Font size and weight) 言語・地域 (Locale) ダークモード・ライトモード (Dark/Light mode) キーボードの有無 (Keyboard availability) 57

Slide 58

Slide 58 text

Configuration changes の影響 (Impact of Configuration Changes) 58

Slide 59

Slide 59 text

デフォルトの動作 (Default Behavior) Configuration change が起きると Activity が再作成される (Activity is recreated when configuration changes occur) 結果 (Result) アプリ内部でのデータの持ち方次第で、アプリの様々な状態が失われる (Depending on how data is handled internally, various app states can be lost due to configuration changes) 59

Slide 60

Slide 60 text

失われるアプリ状態の例 (Examples of Lost App State) スクロール位置 (Scroll position) テキストやチェックボックスなどユーザーが入力した内容 (User input: text, checkboxes, etc.) メディア再生位置 (Media playback position) 表示中のダイアログ (Currently displayed dialogs) 画面遷移 (Screen navigation state) WebView の表示内容 (WebView content) 60

Slide 61

Slide 61 text

Configuration changes 処理の基礎知識 (Fundamentals for Handling Configuration Changes) Activity が提供する状態保持の仕組み (Activity's state preservation mechanism) ViewModel SavedStateHandle View 固有の話 (View-specific topics) Compose 固有の話 (Compose-specific topics) 補足事項 (Additional notes) https://developer.android.com/topic/libraries/architecture/saving-states 61

Slide 62

Slide 62 text

Activity が提供する状態保持の仕組み (Activity's State Preservation Mechanism) 62

Slide 63

Slide 63 text

Activity#onSaveInstanceState override fun onSaveInstanceState(outState: Bundle?) { // Save the user's current game state. outState?.run { putInt(STATE_SCORE, currentScore) putInt(STATE_LEVEL, currentLevel) } // Always call the superclass so it can save the view hierarchy state. super.onSaveInstanceState(outState) } companion object { val STATE_SCORE = "playerScore" val STATE_LEVEL = "playerLevel" } https://developer.android.com/guide/components/activities/activity-lifecycle 63

Slide 64

Slide 64 text

Activity#onRestoreInstanceState override fun onRestoreInstanceState(savedInstanceState: Bundle?) { // Always call the superclass so it can restore the view hierarchy. super.onRestoreInstanceState(savedInstanceState) // Restore state members from saved instance. savedInstanceState?.run { currentScore = getInt(STATE_SCORE) currentLevel = getInt(STATE_LEVEL) } } https://developer.android.com/guide/components/activities/activity-lifecycle 64

Slide 65

Slide 65 text

Activity が提供する状態保持の仕組みの補足 (Additional Notes on Activity's State Preservation) 非常にシンプルな状態保持機能が提供されている (Provides very simple state preservation functionality) 近年の Android アプリ開発では、 (これから紹介する)より便利な方法が提供され ているので、そちらを使う事の方が多い (Modern Android development provides more convenient methods - introduced below - that are more commonly used) 便利な方法の一部は「Activity が提供する状態保持の仕組み」で実現している (Some of these convenient methods are built on top of Activity's state preservation mechanism) 65

Slide 66

Slide 66 text

ViewModel https://developer.android.com/topic/lib raries/architecture/viewmodel 66

Slide 67

Slide 67 text

SavedStateHandle プロセス再作成時でも状態保持が可能 (Enables state preservation even during process recreation) class SavedStateViewModel( private val savedStateHandle: SavedStateHandle ) : ViewModel() { val filteredData: StateFlow> = savedStateHandle.getStateFlow("query") .flatMapLatest { query -> repository.getFilteredData(query) } fun setQuery(query: String) { savedStateHandle["query"] = query } } https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel- savedstate 67

Slide 68

Slide 68 text

View 固有の話 (View-specific Topic) ID 指定すると状態が保持される (State is preserved when ID is specified) https://developer.android.com/guide/fragments/saving-state#view 68

Slide 69

Slide 69 text

Compose 固有の話 (Compose-specific Topics) rememberSavable で状態が保持される (State is preserved with rememberSavable) @Parcelize data class City(val name: String, val country: String) : Parcelable @Composable fun CityScreen() { var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) } } 69

Slide 70

Slide 70 text

補足事項 (Additional Notes) 70

Slide 71

Slide 71 text

About TransactionTooLargeException - 1/3 onSaveInstanceState で保存できるサイズには上限がある (There's a size limit for data saved in onSaveInstanceState) ↓ サイズ超過で TransactionTooLargeException が発生 (Exceeding the size limit causes TransactionTooLargeException) https://developer.android.com/guide/components/activities/parcelables-and-bundles 71

Slide 72

Slide 72 text

About TransactionTooLargeException - 2/3 サイズ上限はアプリプロセス全体で1MB (Size limit is 1MB for the entire app process) SavedStateHandle, rememberSavable、AndroidX 一部ライブラリも同じ領域を使う (SavedStateHandle, rememberSavable, and some AndroidX libraries use the same storage area) アプリ側の保存サイズは 50KB 以下 が推奨 (App should keep saved data under 50KB) https://developer.android.com/guide/components/activities/parcelables-and-bundles 72

Slide 73

Slide 73 text

About TransactionTooLargeException - 3/3 対策:ViewModel の活用、Room や DataStore などストレージでのデータ保持 (Solutions: Use ViewModel, persist data with Room or DataStore) https://developer.android.com/guide/components/activities/parcelables-and-bundles https://developer.android.com/topic/libraries/architecture/saving-states 73

Slide 74

Slide 74 text

Activity の再作成制限について (About Activity Recreation Restriction) - 1/2 android:configChanges に Activity 再作成の制限対象を宣言できる (You can declare targets for Activity recreation restriction in android:configChanges ) https://developer.android.com/guide/topics/resources/runtime-changes#restrict-activity 74

Slide 75

Slide 75 text

Activity の再作成制限について (About Activity Recreation Restriction) - 2/2 アプリ自体が Configuration changes を適切に処理する必要あり (App needs to handle configuration changes itself) View ベースアプリでは困難 (Difficult for View-based apps) 代替リソースは自動ロードされない (Alternative resources won't load automatically) 例: res/layout-sw600dp/ ではなく、常に res/layout/ が使われる (Example: Always uses res/layout/ instead of res/layout-sw600dp/ ) 75

Slide 76

Slide 76 text

「基本的な大画面対応」の進め方 (Steps for Basic Large Screen Support) 1. アプリを全画面で実行する (Run app in full screen) 2. Configuration changes を適切に処理する (Handle configuration changes properly) 3. 重要なタスクフローを完了できるようにする (Enable critical task flows) 76

Slide 77

Slide 77 text

重要なタスクフローを完了できるようにする (Enable Critical Task Flows) 1. 重要なタスクフローをリストアップ (List critical task flows) 2. 問題を検出 (Detect all issues) 3. 問題に対応 (Address issues) 77

Slide 78

Slide 78 text

1. 重要なタスクフローをリストアップ (List Critical Task Flows) U-NEXT アプリの場合 (For U-NEXT app): ログイン・会員登録 (Login & Registration) 購入・定期購入 (Purchase & Subscription) 作品を探す (Search for content) 動画を再生する (Play videos) 本を開く (Open books) 78

Slide 79

Slide 79 text

2. 問題を検出 (Detect all issues) 1. タブレットやフォルダブル端末で重要なタスクフローを実行 (Execute critical task flows on tablets and foldable devices) 2. タスクフロー完了が不可となる、クリティカルな問題を検出 (Detect critical issues that prevent task completion) 79

Slide 80

Slide 80 text

3. 問題に対応 (Address Issues) 残る問題はほとんどが UI レイアウトの問題なはず (Most Remaining Issues Are UI Layout Problems) 80

Slide 81

Slide 81 text

1. 縦スクロール可能にする (Enable Vertical Scrolling) 例:フォルダブル端末でボタンが隠れてしまう (e.g. Buttons hidden on foldable devices) 81

Slide 82

Slide 82 text

82

Slide 83

Slide 83 text

View でスクロール可能にする (Enable Scrolling with View) 83

Slide 84

Slide 84 text

Compose で縦スクロール可能にする (Enable Vertical Scrolling with Compose) Column( modifier = Modifier.verticalScroll(rememberScrollState()) ) { // content } LazyColumn() { // content } 84

Slide 85

Slide 85 text

2. Max 幅を制限する (Limit Maximum Width) 例:タブレット横画面で一つの画像がほぼ画面全体を占めてしまう (e.g. One image occupies almost entire screen on tablet landscape) 85

Slide 86

Slide 86 text

86

Slide 87

Slide 87 text

View で画像の Max 幅を制限する (Limit Image Max Width with View) 87

Slide 88

Slide 88 text

Compose で画像の Max 幅を制限する (Limit Image Max Width with Compose) Image( painter = painterResource(id = R.drawable.sample_image), contentDescription = "", modifier = Modifier .widthIn(max = 800.dp) // <- Set max width .fillMaxWidth() ) 88

Slide 89

Slide 89 text

89

Slide 90

Slide 90 text

View で List item の Max 幅を制限する (Limit List Item Max Width with View) 90

Slide 91

Slide 91 text

Compose で List item の Max 幅を制限する (Limit List Item Max Width with Compose) @Composable fun LimitedWidthLazyColumn() { LazyColumn( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, ) { items(20) { index -> ListItem( modifier = Modifier .widthIn(max = 800.dp) // <- Set max width .fillMaxWidth() ) { // content } } } } 91

Slide 92

Slide 92 text

92

Slide 93

Slide 93 text

目次 (Agenda) 1. 大画面対応の重要性 (Importance of Large Screen Support) 2. 大画面対応のサポートレベル (Large screen support levels) 3. View ベースアプリの大画面対応戦略 (Large Screen Strategy for View-based Apps) 4. 基本的な大画面対応 (Large screen ready) 5. 大画面向けに最適化 (Large screen optimized) 6. 更なる大画面対応 (Further Large Screen Support) 7. まとめ (Summary) 93

Slide 94

Slide 94 text

大画面向けに最適化 (Large screen optimized) 94

Slide 95

Slide 95 text

TIER 2 (better) — Large screen optimized すべての画面サイズ・デバイス設定でレイアウトを最適化 (Layout optimizations for all screen sizes and device configurations) マルチウインドウモードを含む (Including multi-window mode) 外部入力デバイスのサポートを拡張 (Enhanced support for external input devices) https://developer.android.com/docs/quality-guidelines/large-screen-app- quality#tier_2_better_large_screen_optimized 95

Slide 96

Slide 96 text

レイアウトの最適化 (Layout Optimization) Window size classes Navigation rail の導入 (Implementing Navigation rail) リスト表示の改善 (Improving list displays) アプリごとに最適なレイアウトの検討 (Consider optimal layouts for your app) 96

Slide 97

Slide 97 text

Window size classes https://developer.android.com/develop/ui/compose/layouts/adaptive/use-window-size- classes 97

Slide 98

Slide 98 text

Window size class をブレークポイントとして、レイアウトを切り替える (Using Window Size Class as a Breakpoint for Layout Switching) @Composable fun MyApp( windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass ) { // Decide whether to show the top app bar based on window size class. val showTopAppBar = windowSizeClass .isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND) // MyScreen logic is based on the showTopAppBar boolean flag. MyScreen( showTopAppBar = showTopAppBar, /* ... */ ) } https://developer.android.com/develop/ui/compose/layouts/adaptive/use-window-size- classes#manage_layouts_with_window_size_classes 98

Slide 99

Slide 99 text

どの Window size class でレイアウトを切り替えるか? (Which Window Size Class to Use for Layout Switching?) 1. Width window size class - isWidthAtLeastBreakpoint 2. Height window size class - isHeightAtLeastBreakpoint 3. Both window size classes - isAtLeastBreakpoint 99

Slide 100

Slide 100 text

Navigation rail の導入 (Implementing Navigation Rail) ウインドウサイズに応じてナビゲーション UI の切替が推奨 (Recommended to switch navigation UI based on window size) 小さな画面では Navigation bar (Navigation bar for small screens) 大きな画面では Navigation rail (Navigation rail for large screens) 100

Slide 101

Slide 101 text

https://developer.android.com/develop /ui/compose/components/navigation- rail 101

Slide 102

Slide 102 text

NavigationSuiteScaffold( navigationSuiteItems = { AppDestinations.entries.forEach { item( icon = { Icon( it.icon, contentDescription = stringResource(it.contentDescription) ) }, label = { Text(stringResource(it.label)) }, selected = it == currentDestination, onClick = { currentDestination = it } ) } } ) { // Destination content } https://developer.android.com/develop/ui/compose/layouts/adaptive/build-adaptive- navigation 102

Slide 103

Slide 103 text

リスト表示の改善 (Improving List Displays) LazyList -> LazyVerticalGrid ウインドウサイズで 1 行に表示するアイテム数調整 (Adjust number of items per row based on window size) https://developer.android.com/develop/ui/compose/lists#la zy-grids 103

Slide 104

Slide 104 text

GridCells.Fixed のコード例 - 1/2 val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass val columns = when { windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_EXPANDED_LOWER_BOUND) -> 3 windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_MEDIUM_LOWER_BOUND) -> 2 else -> 1 } 104

Slide 105

Slide 105 text

GridCells.Fixed のコード例 - 2/2 LazyVerticalGrid( columns = GridCells.Fixed(columns), modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), ) { items(30) { Spacer( modifier = Modifier .background(MaterialTheme.colorScheme.primaryContainer) .aspectRatio(1f), ) } } 105

Slide 106

Slide 106 text

106

Slide 107

Slide 107 text

GridCells.Adaptive のコード例 LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 120.dp), modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), ) { items(30) { Spacer( modifier = Modifier .background(MaterialTheme.colorScheme.primaryContainer) .aspectRatio(1f), ) } } 107

Slide 108

Slide 108 text

108

Slide 109

Slide 109 text

アプリごとに最適なレイアウトの検討 (Consider Optimal Layouts for Your App) Canonical layouts Large screens gallery 109

Slide 110

Slide 110 text

Canonical layouts List-detail Feed Supporting pane https://developer.android.com/develop/ui/compose/layouts/adaptive/canonical-layouts 110

Slide 111

Slide 111 text

Large screens gallery Social media & communication Media Productivity Shopping Reading Creativity Games https://developer.android.com/large- screens/gallery 111

Slide 112

Slide 112 text

List-detail in U-NEXT app 112

Slide 113

Slide 113 text

List-detail のノウハウ (List-detail Know-how) 113

Slide 114

Slide 114 text

ListDetailPaneScaffold で簡単に実装可能 (Easy implementation with ListDetailPaneScaffold) NavigableListDetailPaneScaffold は既知の不具合あり (NavigableListDetailPaneScaffold has known issues) https://issuetracker.google.com/issues/417881577 114

Slide 115

Slide 115 text

Detail Pane から他の画面に遷移する場合、少し実装が複雑化する (Implementation becomes more complex when navigating to other screens from Detail Pane) 例:Detail Pane 専用の NavHost を追加 (Example: Add a dedicated NavHost for Detail Pane) Navigation 3 の正式リリースを待つのも選択肢の一つ (Waiting for Navigation 3's official release is also an option) 115

Slide 116

Slide 116 text

外部入力デバイスのサポートを拡張 (Extending External Input Device Support) キーボードナビゲーション (Keyboard navigation) Space キーでメディア再生・一時停止 (Space key for media play/pause) マウスホイールでのズーム (Mouse wheel zoom) 116

Slide 117

Slide 117 text

キーボードナビゲーション (Keyboard Navigation) キーボード操作だけで主要なタスクフローを実行可能とする (Enable main task flows with keyboard only) Tab、矢印キーでフォーカス移動 (Focus navigation with Tab and arrow keys) 極力、矢印キーのみで完結するのが望ましい (Preferably complete with arrow keys only) Modifier.onClick で Enter にも反応する (Modifier.onClick responds to Enter key) 実際にキーボードだけで操作して問題点を洗い出してから改善 (Test with keyboard only to identify and fix issues) 117

Slide 118

Slide 118 text

Modifier.focusProperties でフォーカス移動先を制御する (Control Focus Navigation with Modifier.focusProperties) val focusRequester = remember { FocusRequester() } Row { Button( onClick = {}, modifier = Modifier.weight(1f) .focusProperties { next = focusRequester // when Tab key pressed right = focusRequester // when Right arrow key pressed } ) { Text("1") } Button( onClick = {}, modifier = Modifier.weight(1f) .focusRequester(focusRequester) ) { Text("2") } } 118

Slide 119

Slide 119 text

https://developer.android.com/develop /ui/compose/touch- input/focus/change-focus-behavior 119

Slide 120

Slide 120 text

Space キーでメディア再生・一時停止 (Space Key for Media Play/Pause) 120

Slide 121

Slide 121 text

Modifier.onKeyEvent でスペースキー操作を処理する (Handle Space Key with Modifier.onKeyEvent) val focusRequester = remember { FocusRequester() } Surface( modifier = Modifier.focusRequester(focusRequester).focusable() .onKeyEvent { keyEvent -> when { keyEvent.type != KeyEventType.KeyDown -> false keyEvent.key == Key.Spacebar -> { // play / pause true } else -> false } }, ) { LaunchedEffect(Unit) { focusRequester.requestFocus() } Text("hello world") } 121

Slide 122

Slide 122 text

マウスホイールでのズーム (Mouse Wheel Zoom) Ctrl キーを押した状態でのマウスホイール操作でズーム (Zoom with mouse wheel while holding Ctrl key) 122

Slide 123

Slide 123 text

https://github.com/usuiat/Zoomable Image( painter = painterResource(resource = R.drawable.sample_imag), contentDescription = "Sample image", modifier = Modifier.zoomable(zoomState = rememberZoomState()), ) 123

Slide 124

Slide 124 text

目次 (Agenda) 1. 大画面対応の重要性 (Importance of Large Screen Support) 2. 大画面対応のサポートレベル (Large screen support levels) 3. View ベースアプリの大画面対応戦略 (Large Screen Strategy for View-based Apps) 4. 基本的な大画面対応 (Large screen ready) 5. 大画面向けに最適化 (Large screen optimized) 6. 更なる大画面対応 (Further Large Screen Support) 7. まとめ (Summary) 124

Slide 125

Slide 125 text

更なる大画面対応 (Further Large Screen Support) 125

Slide 126

Slide 126 text

TIER 1 (best) — Large screen differentiated 大画面デバイス向けにデザインされたユーザーエクスペリエンスを提供 (Provides user experience designed for large screen devices) 該当する場合、追加機能のサポート (Where applicable, additional feature support) マルチタスク、フォルダブルデバイスの形状、ドラッグ&ドロップ、スタイラ ス入力 (Multitasking, Foldable postures, Drag and drop, Stylus input) https://developer.android.com/docs/quality-guidelines/large-screen-app- quality#tier_1_best_large_screen_differentiated 126

Slide 127

Slide 127 text

Large screen differentiated App が提供する機能の例 (Example Features of Large Screen Differentiated Apps) Picture-in-picture Multiple instances Tabletop posture Dual display Drag and drop Draw and write within the app using a stylus https://developer.android.com/docs/quality-guidelines/large-screen-app- quality#large_screen_differentiated 127

Slide 128

Slide 128 text

U-NEXT アプリ がサポートしている機能 (Features Supported by U-NEXT App) Picture-in-Picture Tabletop posture 128

Slide 129

Slide 129 text

Picture-in-Picture 129

Slide 130

Slide 130 text

Picture-in-Picture 小さなウインドウでアプリを利用可能 (App available in a small window) 主に動画の再生に使用 (Primarily used for video playback) Google Map アプリなど動画再生以外での使用例あり (Also used in non-video apps like Google Maps) Android 8.0 以上でサポート (Supported on Android 8.0+) https://developer.android.com/develop/ui/views/picture-in- picture 130

Slide 131

Slide 131 text

https://developer.android.com/develop /ui/compose/system/picture-in-picture 131

Slide 132

Slide 132 text

Picture-in-Picture 対応のノウハウ (Picture-in-Picture Implementation Tips) 132

Slide 133

Slide 133 text

Android 12 以上と 12 未満の両方で動作確認する (Test on both Android 12+ and below) OS 側の実装が大きく変わった (OS implementation changed significantly) setAutoEnterEnabled を有効にするかどうかでも挙動が変わる (Behavior varies with setAutoEnterEnabled setting) 133

Slide 134

Slide 134 text

Picture-in-Picture ウインドウ表示中はタッチイベントを受け取れない (App cannot receive touch events during PiP) ダイアログなど、ユーザーのタッチ操作が必須な UI 表示を避ける (Avoid UI that requires touch input like dialogs) 再生・停止などの操作は RemoteAction として定義 (Define play/stop controls as RemoteAction) 134

Slide 135

Slide 135 text

Android 8.0 以上でもサポートしていない端末がある (Some devices don't support PiP even on Android 8.0+) hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) で判定 (Check with hasSystemFeature) ただし、一部端末は嘘を返すので、 activity.enterPictureInPictureMode は try catch しておくのが安全 (Some devices return false positives, so wrap enterPictureInPictureMode in try-catch) 135

Slide 136

Slide 136 text

Picture-in-Picture ウインドウ表示中のアプリ起動ケースを考慮 (Consider app launch from launcher during PiP) SingleActivity 化しないと、アプリ起動で自動 PiP 終了は実現不可 (Auto-exit PiP on app launch requires SingleActivity architecture) 136

Slide 137

Slide 137 text

Tabletop posture 137

Slide 138

Slide 138 text

Tabletop posture フォルダブル端末メイン画面を半分程度開いた状態 (Foldable device's main screen half-opened) ユーザーが手でスマフォを持たなくて良い (Users don't need to hold the phone) メディア視聴や写真撮影・ビデオ通話に最適 (Ideal for media viewing, photography, and video calls) https://developer.android.com/develop/ui/compose/layouts /adaptive/foldables/make-your-app-fold-aware 138

Slide 139

Slide 139 text

object TableTopModeTracker { fun isTableTop(context: Context): Flow { return WindowInfoTracker.getOrCreate(context) .windowLayoutInfo(context) .map { it.isTableTop } } } val WindowLayoutInfo.isTableTop: Boolean get() { val foldingFeature = displayFeatures .filterIsInstance() .firstOrNull() ?: return false return foldingFeature.orientation == FoldingFeature.Orientation.HORIZONTAL && foldingFeature.state == FoldingFeature.State.HALF_OPENED } 139

Slide 140

Slide 140 text

@Composable fun PlayerScreen() { val isTableTopPosture by TableTopModeTracker.isTableTop(LocalContext.current) .collectAsState(false) if(isTableTopPosture) { // Tabletop posture layout } else { // Normal layout } } 140

Slide 141

Slide 141 text

目次 (Agenda) 1. 大画面対応の重要性 (Importance of Large Screen Support) 2. 大画面対応のサポートレベル (Large screen support levels) 3. View ベースアプリの大画面対応戦略 (Large Screen Strategy for View-based Apps) 4. 基本的な大画面対応 (Large screen ready) 5. 大画面向けに最適化 (Large screen optimized) 6. 更なる大画面対応 (Further Large Screen Support) 7. まとめ (Summary) 141

Slide 142

Slide 142 text

まとめ (Summary) 142

Slide 143

Slide 143 text

大画面対応は待ったなし (Large Screen Support Can't Wait) 2025 Target SDK 36 で 画面回転・アスペクト比制限が無視される (Orientation and aspect ratio restrictions ignored for API level 36) 2026 Target SDK 37 で opt-out 不可 (No opt-out for API level 37) 今すぐ対応を始めましょう! (Start adapting now!) 143

Slide 144

Slide 144 text

段階的アプローチの推奨 (Recommended Phased Approach) 144

Slide 145

Slide 145 text

Step 1: TIER 3 対応 - View でも可能 (TIER 3 Support - Possible with View) 全画面実行 (Full screen execution) Configuration changes 処理 (Handle configuration changes) スクロール対応と Max 幅制限 (Scrolling and max width constraints) 145

Slide 146

Slide 146 text

Step 2: TIER 2 以上を目指して大画面に最適化 (Step 2: Optimize for Large Screens Targeting TIER 2+) 柔軟なレイアウト対応 (Flexible layout support) キーボード・マウス対応 (Keyboard and mouse support) 146

Slide 147

Slide 147 text

Android XR への準備 (Preparing for Android XR) 147

Slide 148

Slide 148 text

Large screen app quality と Android XR の関係 (Relationship between Large screen app quality and Android XR) TIER 2 達成 = Android XR compatible large screen app 大画面対応は XR 対応の第一歩 (Large screen support is the first step to XR) Android XR デバイスでも快適に動作するアプリに (Apps that work on Android XR devices) より多くのユーザーにリーチ (Reach more users) 148

Slide 149

Slide 149 text

今日から試せること (What You Can Try Today) 1. AndroidManifest.xml の変更 (Modify AndroidManifest.xml) 画面向き指定の削除 (Remove orientation specification) アスペクト比指定の削除 (Remove aspect ratio specification) リサイズ制限の削除 (Remove resizeable restriction) 2. 大画面デバイスでの動作確認 (Test on large screen devices) 横画面でのレイアウト確認 (Check layout in landscape mode) デバイスを回転させてテスト (Rotate device and test) 画面分割でテスト (Test with split screen) 149

Slide 150

Slide 150 text

ご清聴ありがとうございました (Thank You) 150

Slide 151

Slide 151 text

リソース (Resources) 公式ドキュメント (Official Documentation) Large screen app quality guidelines Android 16 behavior changes Window Size Classes 公式サンプルコード (Official Sample Code) Now in Android - Canonical layouts の実装例 Compose samples - Adaptive layouts 151