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

DroidKaigi 2020: System UIをコントロールして、 画面を最大限に生かしたアプリを構築する / Build apps that make the best uses of device screens by controlling System UI

DroidKaigi 2020: System UIをコントロールして、 画面を最大限に生かしたアプリを構築する / Build apps that make the best uses of device screens by controlling System UI

端末の画面を最大限に活かし、より良い体験を提供するために、System UI(Status Bar、Navigation Bar)を考慮することは非常に重要です。
しかし、Android Q、Q以前などでのバージョンごとの差異、ノッチ対応、画面構成によっては、WindowInsetsを取得する必要があるなど、考慮しなければならない点が多くあります。

この発表では、最初にSystem UIをコントロールするために抑えておくべき要素を、実際の挙動を見ながら、1つ1つ理解していきます。
具体的には、次のことを説明します。

- fitsSystemWindowsの挙動について
- ThemeからSystem UIの設定をする
- Navigation Component(Single Activity)との兼ね合い
- WindowInsetsを取り、動的にSystem UIを調整する
- FloatingActionButtonとの兼ね合い
- systemUiVisibilityについて
- ノッチ端末(cutout)への対応

最後に、実践的な例として、Google I/OなどのアプリのSystem UIをどのようにすれば構築出来るかを説明します。

System UIの基礎 〜 実践までを理解することで、画面を最大限に生かした没入感のあるアプリを実装出来るようになることを目指します。

2793f1fe246c9299c2416062d22bfb99?s=128

Sato Shun

April 02, 2020
Tweet

Transcript

  1. DroidKaigi 2020 Sato Shun GitHub: satoshun Twitter: @stsn_jp CyberAgent, Inc.

    System UIΛίϯτϩʔϧͯ͠ɺ ը໘Λ࠷େݶʹੜ͔ͨ͠ΞϓϦΛߏங͢Δ
  2. ͜ͷൃදͷલఏ • API 21Ҏ߱Λ૝ఆ͍ͯ͠·͢ • ͜ͷൃදͰ͸ɺAPI 21͕API 1ʹͳΓ·͢

  3. • Edge to Edgeͱ͸? • Edge to EdgeΛͳͥ΍Δ͔? • Edge

    to EdgeΛͲͷΑ͏ʹ΍Δ͔ʁ • ΧελϜView͔ΒֶͿ • ଞͷΞϓϦ͔ΒֶͿ
  4. Edge to Edgeͱ͸?

  5. • ը໘શମΛίϯςϯπྖҬʹ • System UIͷഎޙʹίϯςϯπ Λඳը https://medium.com/androiddevelopers/gesture-navigation-going-edge-to-edge-812f62e4e83e

  6. Edge to EdgeΛͳͥ΍Δ͔?

  7. Edge to EdgeΛͳͥ΍Δ͔? • ը໘શମΛ࢖͏͜ͱͰɺΑΓ຅ೖײͷ͋ΔΞϓϦΛߏஙͰ͖ Δ • AndroidϓϥοτϑΥʔϜશମͷྲྀΕ • API

    29(Q)Ҏ߱Ͱ͸ɺڧ͘નΊ͍ͯΔ
  8. Edge to EdgeΛͲͷΑ͏ʹ΍Δ͔

  9. Edge to EdgeΛͲͷΑ͏ʹ΍Δ͔ • SystemUiVisibility • System UIͷഎܠ৭ • fitsSystemWindows

    • WindowInsets
  10. SystemUiVisibility ͱ͸? • System UIʢstatus bar / navigation barʣͷݟͨ໨ɺৼΔ෣͍ Λมߋ͢Δ

  11. SystemUiVisibility ͱ͸? • System UIʢstatus bar / navigation barʣͷݟͨ໨ɺৼΔ෣͍ Λมߋ͢Δ

    • Immersive modeʢ຅ೖϞʔυʣ
  12. SystemUiVisibility ͱ͸? • System UIʢstatus bar / navigation barʣͷݟͨ໨ɺৼΔ෣͍ Λมߋ͢Δ

    • Immersive modeʢ຅ೖϞʔυʣ
  13. SystemUiVisibility ͱ͸? • System UIʢstatus bar / navigation barʣͷݟͨ໨ɺৼΔ෣͍ Λมߋ͢Δ

    • Immersive modeʢ຅ೖϞʔυʣ
  14. SystemUiVisibility ͱ͸? • System UIʢstatus bar / navigation barʣͷݟͨ໨ɺৼΔ෣͍ Λมߋ͢Δ

    • Immersive modeʢ຅ೖϞʔυʣ • status bar / navigation barͷഎޙʹඳը
  15. SystemUiVisibility ͱ͸? • System UIʢstatus bar / navigation barʣͷݟͨ໨ɺৼΔ෣͍ Λมߋ͢Δ

    • Immersive modeʢ຅ೖϞʔυʣ • status bar / navigation barͷഎޙʹඳը
  16. status bar navigation bar

  17. <ConstraintLayout> <ImageView /> </ConstraintLayout>

  18. <ConstraintLayout> <ImageView /> </ConstraintLayout>

  19. <ConstraintLayout> <ImageView /> </ConstraintLayout>

  20. <ConstraintLayout> <ImageView /> </ConstraintLayout> γεςϜ͕ViewΛ ૠೖ͢Δ

  21. None
  22. None
  23. None
  24. None
  25. paddingTopɺmarginBottom͕ ηοτ͞Ε͍ͯΔ

  26. paddingTopɺmarginBottom͕ ηοτ͞Ε͍ͯΔ

  27. paddingTopɺmarginBottom͕ ηοτ͞Ε͍ͯΔ -> ίϯςϯπΛඳը͢Δ͜ͱ͸ग़དྷͳ͍

  28. paddingTopɺmarginBottom͕ ηοτ͞Ε͍ͯΔ -> ίϯςϯπΛඳը͢Δ͜ͱ͸ग़དྷͳ͍ ͜ͷpaddingTopɺmarginBottomͷ஋Λ ্ॻ͖͢Δඞཁ͕͋Δ

  29. None
  30. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  31. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  32. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  33. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  34. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  35. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  36. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  37. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  38. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  39. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  40. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  41. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  42. None
  43. None
  44. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  45. SystemUiVisibility·ͱΊ • SYSTEM_UI_FLAG_LAYOUT_FULLSCREENɺ SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION • 2ͭΛ૊Έ߹ΘͤΔ͜ͱͰɺmarginɺpaddingΛ0ʹઃఆ͢Δ͜ͱ ͕ग़དྷΔ • ίϯςϯπΛSystem UIͷഎޙʹඳը͢Δ͜ͱ͕ग़དྷΔ

    • SYSTEM_UI_FLAG_LAYOUT_STABLE΋෇͚ͨ΄͏͕ྑ͍ • ৄ͘͠͸ɺDroidKaigi2020 ৄղ WindowInsetsΛݟ͍ͯͩ͘͞
  46. System UIͷഎܠ৭

  47. None
  48. None
  49. window.statusBarColor = … <item name="android:statusBarColor">#B3FFFFFF</item>

  50. window.statusBarColor = … <item name="android:statusBarColor">#B3FFFFFF</item>

  51. None
  52. view.systemUiVisibility = ... View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR <item name="android:windowLightStatusBar">true</item>

  53. view.systemUiVisibility = ... View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR <item name="android:windowLightStatusBar">true</item>

  54. API23+ view.systemUiVisibility = ... View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR <item name="android:windowLightStatusBar">true</item>

  55. API23+ API23ະຬͰ͸ɺ Lightʢ໌Δ͍ʣ৭Λstatus barͷ എܠ৭ʹऔΔͱࢹೝੑ͕ஶ͘͠ ௿Լ͢Δ͜ͱΛҙຯ͢Δ view.systemUiVisibility = ... View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR

    <item name="android:windowLightStatusBar">true</item>
  56. None
  57. None
  58. window.navigationBarColor = … <item name="android:navigationBarColor">#B3FFFFFF</item>

  59. window.navigationBarColor = … <item name="android:navigationBarColor">#B3FFFFFF</item>

  60. None
  61. view.systemUiVisibility = ... View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR <item name="android:windowLightNavigationBar">true</item>

  62. view.systemUiVisibility = ... View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR <item name="android:windowLightNavigationBar">true</item>

  63. API27+ view.systemUiVisibility = ... View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR <item name="android:windowLightNavigationBar">true</item>

  64. API27+ API27ະຬͰ͸ɺ Lightʢ໌Δ͍ʣ৭Λnavigationͷ എܠ৭ʹऔΔͱࢹೝੑ͕ஶ͘͠ ௿Լ͢Δ͜ͱΛҙຯ͢Δ view.systemUiVisibility = ... View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR <item

    name="android:windowLightNavigationBar">true</item>
  65. None
  66. <item name=“android:navigationBarColor">transparent</item>

  67. <item name=“android:navigationBarColor">transparent</item>

  68. API29+͔ΒtransparentΛ ࢦఆͨ͠৔߹ʹɺ ࣗಈతʹഎܠ৭Λઃఆͯ͘͠ΕΔ Α͏ʹͳͬͨ <item name=“android:navigationBarColor">transparent</item>

  69. API29+͔ΒtransparentΛ ࢦఆͨ͠৔߹ʹɺ ࣗಈతʹഎܠ৭Λઃఆͯ͘͠ΕΔ Α͏ʹͳͬͨ <item name=“android:navigationBarColor">transparent</item> <item name="android:enforceNavigationBarContrast">false</item> <item name="android:enforceStatusBarContrast">false</item>

  70. <item name=“android:navigationBarColor">transparent</item> API29+͔ΒtransparentΛ ࢦఆͨ͠৔߹ʹɺ ࣗಈతʹഎܠ৭Λઃఆͯ͘͠ΕΔ Α͏ʹͳͬͨ <item name="android:enforceNavigationBarContrast">false</item> <item name="android:enforceStatusBarContrast">false</item>

  71. System UIഎܠ৭ͷ·ͱΊ • API23+: windowLightStatusBarͷ௥Ճ • API27+: windowLightNavigationBarͷ௥Ճ • API29+:

    enforceNavigationBarContrastɺ enforceStatusBarContrastͷ௥Ճ • transparentͷͱ͖ɺ͍͍ײ͡ʹഎܠ৭Λௐ੔ͯ͘͠ΕΔ
  72. fitsSystemWindows

  73. fitsSystemWindowsͱ͸? • System UIΛߟྀͯ͠ɺΞϓϦέʔγϣϯίϯςϯπΛ഑ஔͯ͠ ͘ΕΔ • σϑΥϧτͷಈ࡞ͱɺΧελϚΠζͨ͠ಈ࡞͕͋Δ • ೉͍͠ଐੑͷ1ͭʢݸਓతͳײ૝ʣ

  74. None
  75. <LinearLayout> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

  76. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  77. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  78. view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION ্෦ɺԼ෦ͷmarginɺpadding͕θϩʹͳΔͷͰɺ ཁૉ͕ΊΓࠐΜͰ͠·͏

  79. <LinearLayout> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

  80. <LinearLayout android:fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

  81. <LinearLayout android:fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

  82. <LinearLayout android:fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

  83. <LinearLayout android:fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

  84. <LinearLayout android:fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout> fitsSystemWindows͸ɺ

    paddingTopɺpaddingBottomΛઃఆ͢Δ
  85. <LinearLayout android:fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

  86. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

  87. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

  88. ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button />

    </LinearLayout>
  89. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

    ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ fitsSystemWindows͸σϑΥϧτͰɺ ਂ͞༏ઌ୳ࡧΛ͠ɺݟ͔ͭͬͨ࠷ॳͷΈ༗ޮ
  90. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

    ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ fitsSystemWindows͸σϑΥϧτͰɺ ਂ͞༏ઌ୳ࡧΛ͠ɺݟ͔ͭͬͨ࠷ॳͷΈ༗ޮ View View View View View
  91. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

    ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ fitsSystemWindows͸σϑΥϧτͰɺ ਂ͞༏ઌ୳ࡧΛ͠ɺݟ͔ͭͬͨ࠷ॳͷΈ༗ޮ View View View View View
  92. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

    ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ fitsSystemWindows͸σϑΥϧτͰɺ ਂ͞༏ઌ୳ࡧΛ͠ɺݟ͔ͭͬͨ࠷ॳͷΈ༗ޮ View View View View View
  93. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

    ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ fitsSystemWindows͸σϑΥϧτͰɺ ਂ͞༏ઌ୳ࡧΛ͠ɺݟ͔ͭͬͨ࠷ॳͷΈ༗ޮ View View View View View
  94. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

    ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ fitsSystemWindows͸σϑΥϧτͰɺ ਂ͞༏ઌ୳ࡧΛ͠ɺݟ͔ͭͬͨ࠷ॳͷΈ༗ޮ View View View View View
  95. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

    ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ fitsSystemWindows͸σϑΥϧτͰɺ ਂ͞༏ઌ୳ࡧΛ͠ɺݟ͔ͭͬͨ࠷ॳͷΈ༗ޮ View View View View View
  96. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

    ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ fitsSystemWindows͸σϑΥϧτͰɺ ਂ͞༏ઌ୳ࡧΛ͠ɺݟ͔ͭͬͨ࠷ॳͷΈ༗ޮ
  97. <LinearLayout android:fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout> fitsSystemWindows͸σϑΥϧτͰɺ

    ਂ͞༏ઌ୳ࡧΛ͠ɺݟ͔ͭͬͨ࠷ॳͷΈ༗ޮ ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ
  98. fitsSystemWindows·ͱΊ • σϑΥϧτͷڍಈ • System UIʹඃΒͳ͍Α͏ʹɺpaddingTopɺpaddingBottomΛ ઃఆ͢Δ • ਂ͞༏ઌ୳ࡧͰݟ͔ͭͬͨɺ࠷ॳͷViewͷΈ༗ޮʹͳΔ ΋ͬͱॊೈʹ࢖͍͍ͨཁٻ

  99. WindowInsets

  100. WindowInsetsͱ͸? • System UIͷ෯ɺ઎ΊΔྖҬ • WindowInsetsΛ࢖͏͜ͱͰɺfitsSystemWindowsΛΑΓॊೈ ʹΧελϚΠζͯ͠࢖͏͜ͱ͕ग़དྷΔ

  101. None
  102. view.setOnApplyWindowInsetsListener { v, insets -> insets }

  103. view.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom

    = insets.systemWindowInsetBottom ) insets }
  104. view.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom

    = insets.systemWindowInsetBottom ) insets }
  105. view.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom

    = insets.systemWindowInsetBottom ) insets }
  106. view.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom

    = insets.systemWindowInsetBottom ) insets } class WindowInsets { Insets getSystemWindowInsets() Insets getStableInsets() Insets getSystemGestureInsets() Insets getMandatorySystemGestureInsets() Insets getTappableElementInsets() DisplayCutout getDisplayCutout() }
  107. view.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom

    = insets.systemWindowInsetBottom ) insets }
  108. view.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom

    = insets.systemWindowInsetBottom ) insets } child.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom ) insets }
  109. view.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom

    = insets.systemWindowInsetBottom ) insets } child.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom ) insets }
  110. view.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom

    = insets.systemWindowInsetBottom ) insets.consumeSystemWindowInsets() } child.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom ) insets }
  111. view.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom

    = insets.systemWindowInsetBottom ) insets.consumeSystemWindowInsets() } child.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom ) insets }
  112. view.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom

    = insets.systemWindowInsetBottom ) insets.consumeSystemWindowInsets() } child.setOnApplyWindowInsetsListener { v, insets -> v.updatePadding( top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom ) insets }
  113. public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { try { mPrivateFlags3 |= PFLAG3_APPLYING_INSETS;

    if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) { return mListenerInfo.mOnApplyWindowInsetsListener.onApplyWindowInsets(this, insets); } else { return onApplyWindowInsets(insets); } } finally { mPrivateFlags3 &= ~PFLAG3_APPLYING_INSETS; } }
  114. public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { try { mPrivateFlags3 |= PFLAG3_APPLYING_INSETS;

    if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) { return mListenerInfo.mOnApplyWindowInsetsListener.onApplyWindowInsets(this, insets); } else { return onApplyWindowInsets(insets); } } finally { mPrivateFlags3 &= ~PFLAG3_APPLYING_INSETS; } }
  115. public WindowInsets onApplyWindowInsets(WindowInsets insets) { if ((mPrivateFlags3 & PFLAG3_FITTING_SYSTEM_WINDOWS) ==

    0) { if (fitSystemWindows(insets.getSystemWindowInsetsAsRect())) { return insets.consumeSystemWindowInsets(); } } else { if (fitSystemWindowsInt(insets.getSystemWindowInsetsAsRect())) { return insets.consumeSystemWindowInsets(); } } return insets; }
  116. public WindowInsets onApplyWindowInsets(WindowInsets insets) { if ((mPrivateFlags3 & PFLAG3_FITTING_SYSTEM_WINDOWS) ==

    0) { if (fitSystemWindows(insets.getSystemWindowInsetsAsRect())) { return insets.consumeSystemWindowInsets(); } } else { if (fitSystemWindowsInt(insets.getSystemWindowInsetsAsRect())) { return insets.consumeSystemWindowInsets(); } } return insets; } σϑΥϧτͷfitsSystemWindowsͷ৔߹ɺInsetsΛফඅ͢Δ
  117. public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { try { mPrivateFlags3 |= PFLAG3_APPLYING_INSETS;

    if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) { return mListenerInfo.mOnApplyWindowInsetsListener.onApplyWindowInsets(this, insets); } else { return onApplyWindowInsets(insets); } } finally { mPrivateFlags3 &= ~PFLAG3_APPLYING_INSETS; } }
  118. public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { try { mPrivateFlags3 |= PFLAG3_APPLYING_INSETS;

    if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) { return mListenerInfo.mOnApplyWindowInsetsListener.onApplyWindowInsets(this, insets); } else { return onApplyWindowInsets(insets); } finally { mPrivateFlags3 &= ~PFLAG3_APPLYING_INSETS; } }
  119. public void setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener listener) { getListenerInfo().mOnApplyWindowInsetsListener = listener; } Listener͕ొ࿥͞Ε͍ͯΔͱɺ

    fitsSystemWindowsͷಈ࡞ΛΧελϚΠζ͢Δ ͜ͱ͕ग़དྷΔ view.setOnApplyWindowInsetsListener { v, insets -> … }
  120. WindowInsets·ͱΊ • System UIͷ෯ɺྖҬΛऔಘͰ͖Δ • ফඅͨ͠৔߹ɺͦΕҎ߱ʹWindowInsets͕఻ൖ͠ͳ͍ • σϑΥϧτͷfitsSystemWindowsͷ৔߹ɺඞͣফඅ͢Δ • setOnApplyWindowListener͔ΒɺfitsSystemWindowsͷσ

    ϑΥϧτͷڍಈΛม͑Δ͜ͱ͕Մೳ
  121. ΧελϜView͔ΒֶͿ

  122. <CoordinatorLayout> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout>

  123. <CoordinatorLayout fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout>

  124. <CoordinatorLayout fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout>

  125. <CoordinatorLayout fitsSystemWindows> <AppBarLayout fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout>

  126. <CoordinatorLayout fitsSystemWindows> <AppBarLayout fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout>

  127. <CoordinatorLayout fitsSystemWindows> <AppBarLayout fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <BottomAppBar fitsSystemWindows />

    </CoordinatorLayout>
  128. <CoordinatorLayout fitsSystemWindows> <AppBarLayout fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <BottomAppBar fitsSystemWindows />

    </CoordinatorLayout>
  129. <CoordinatorLayout fitsSystemWindows> <AppBarLayout fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <BottomAppBar fitsSystemWindows />

    </CoordinatorLayout> setOnApplyWindowListenerΛ࢖ͬͯɺ σϑΥϧτͷڍಈΛม͍͑ͯΔ ·ͨɺWindowInsets͸ফඅ͍ͯ͠ͳ͍
  130. private void setupForInsets() { if (ViewCompat.getFitsSystemWindows(this)) { if (mApplyWindowInsetsListener ==

    null) { mApplyWindowInsetsListener = new androidx.core.view.OnApplyWindowInsetsListener() { @Override public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { return setWindowInsets(insets); } }; } // First apply the insets listener ViewCompat.setOnApplyWindowInsetsListener(this, mApplyWindowInsetsListener);
  131. private void setupForInsets() { if (ViewCompat.getFitsSystemWindows(this)) { if (mApplyWindowInsetsListener ==

    null) { mApplyWindowInsetsListener = new androidx.core.view.OnApplyWindowInsetsListener() { @Override public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { return setWindowInsets(insets); } }; } // First apply the insets listener ViewCompat.setOnApplyWindowInsetsListener(this, mApplyWindowInsetsListener);
  132. private void setupForInsets() { if (ViewCompat.getFitsSystemWindows(this)) { if (mApplyWindowInsetsListener ==

    null) { mApplyWindowInsetsListener = new androidx.core.view.OnApplyWindowInsetsListener() { @Override public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { return setWindowInsets(insets); } }; } // First apply the insets listener ViewCompat.setOnApplyWindowInsetsListener(this, mApplyWindowInsetsListener);
  133. private void setupForInsets() { if (ViewCompat.getFitsSystemWindows(this)) { if (mApplyWindowInsetsListener ==

    null) { mApplyWindowInsetsListener = new androidx.core.view.OnApplyWindowInsetsListener() { @Override public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { return setWindowInsets(insets); } }; } // First apply the insets listener ViewCompat.setOnApplyWindowInsetsListener(this, mApplyWindowInsetsListener);
  134. final WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) { if (!ObjectsCompat.equals(mLastInsets, insets)) { mLastInsets

    = insets; // Now dispatch to the Behaviors insets = dispatchApplyWindowInsetsToBehaviors(insets); requestLayout(); } return insets; }
  135. <CoordinatorLayout fitsSystemWindows> <AppBarLayout fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <BottomAppBar fitsSystemWindows />

    </CoordinatorLayout> final WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) { if (!ObjectsCompat.equals(mLastInsets, insets)) { mLastInsets = insets; // Now dispatch to the Behaviors insets = dispatchApplyWindowInsetsToBehaviors(insets); requestLayout(); } return insets; }
  136. if (mLastInsets != null && ViewCompat.getFitsSystemWindows(this) && !ViewCompat.getFitsSystemWindows(child)) { parent.left

    += mLastInsets.getSystemWindowInsetLeft(); parent.top += mLastInsets.getSystemWindowInsetTop(); parent.right -= mLastInsets.getSystemWindowInsetRight(); parent.bottom -= mLastInsets.getSystemWindowInsetBottom(); } final Rect out = acquireTempRect(); GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(), child.getMeasuredHeight(), parent, out, layoutDirection); child.layout(out.left, out.top, out.right, out.bottom); private void layoutChild(…)
  137. if (mLastInsets != null && ViewCompat.getFitsSystemWindows(this) && !ViewCompat.getFitsSystemWindows(child)) { parent.left

    += mLastInsets.getSystemWindowInsetLeft(); parent.top += mLastInsets.getSystemWindowInsetTop(); parent.right -= mLastInsets.getSystemWindowInsetRight(); parent.bottom -= mLastInsets.getSystemWindowInsetBottom(); } final Rect out = acquireTempRect(); GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(), child.getMeasuredHeight(), parent, out, layoutDirection); child.layout(out.left, out.top, out.right, out.bottom); private void layoutChild(…)
  138. if (mLastInsets != null && ViewCompat.getFitsSystemWindows(this) && !ViewCompat.getFitsSystemWindows(child)) { parent.left

    += mLastInsets.getSystemWindowInsetLeft(); parent.top += mLastInsets.getSystemWindowInsetTop(); parent.right -= mLastInsets.getSystemWindowInsetRight(); parent.bottom -= mLastInsets.getSystemWindowInsetBottom(); } final Rect out = acquireTempRect(); GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(), child.getMeasuredHeight(), parent, out, layoutDirection); child.layout(out.left, out.top, out.right, out.bottom); <CoordinatorLayout> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout>
  139. if (mLastInsets != null && ViewCompat.getFitsSystemWindows(this) && !ViewCompat.getFitsSystemWindows(child)) { parent.left

    += mLastInsets.getSystemWindowInsetLeft(); parent.top += mLastInsets.getSystemWindowInsetTop(); parent.right -= mLastInsets.getSystemWindowInsetRight(); parent.bottom -= mLastInsets.getSystemWindowInsetBottom(); } final Rect out = acquireTempRect(); GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(), child.getMeasuredHeight(), parent, out, layoutDirection); child.layout(out.left, out.top, out.right, out.bottom); <CoordinatorLayout fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout>
  140. ViewCompat.setOnApplyWindowInsetsListener( this, new androidx.core.view.OnApplyWindowInsetsListener() { @Override public WindowInsetsCompat onApplyWindowInsets(View v,

    WindowInsetsCompat insets) { return onWindowInsetChanged(insets); } }); class AppBarLayout
  141. ViewCompat.setOnApplyWindowInsetsListener( this, new androidx.core.view.OnApplyWindowInsetsListener() { @Override public WindowInsetsCompat onApplyWindowInsets(View v,

    WindowInsetsCompat insets) { return onWindowInsetChanged(insets); } });
  142. WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) { WindowInsetsCompat newInsets = null; if

    (ViewCompat.getFitsSystemWindows(this)) { newInsets = insets; } … }
  143. WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) { WindowInsetsCompat newInsets = null; if

    (ViewCompat.getFitsSystemWindows(this)) { newInsets = insets; } … }
  144. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final

    int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode != MeasureSpec.EXACTLY && ViewCompat.getFitsSystemWindows(this) && shouldOffsetFirstChild()) { int newHeight = getMeasuredHeight(); switch (heightMode) { case MeasureSpec.AT_MOST: newHeight = MathUtils.clamp( getMeasuredHeight() + getTopInset(), 0, MeasureSpec.getSize(heightMeasureSpec)); break; …
  145. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final

    int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode != MeasureSpec.EXACTLY && ViewCompat.getFitsSystemWindows(this) && shouldOffsetFirstChild()) { int newHeight = getMeasuredHeight(); switch (heightMode) { case MeasureSpec.AT_MOST: newHeight = MathUtils.clamp( getMeasuredHeight() + getTopInset(), 0, MeasureSpec.getSize(heightMeasureSpec)); break; …
  146. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final

    int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode != MeasureSpec.EXACTLY && ViewCompat.getFitsSystemWindows(this) && shouldOffsetFirstChild()) { int newHeight = getMeasuredHeight(); switch (heightMode) { case MeasureSpec.AT_MOST: newHeight = MathUtils.clamp( getMeasuredHeight() + getTopInset(), 0, MeasureSpec.getSize(heightMeasureSpec)); break; … <CoordinatorLayout fitsSystemWindows> <AppBarLayout> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout>
  147. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final

    int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode != MeasureSpec.EXACTLY && ViewCompat.getFitsSystemWindows(this) && shouldOffsetFirstChild()) { int newHeight = getMeasuredHeight(); switch (heightMode) { case MeasureSpec.AT_MOST: newHeight = MathUtils.clamp( getMeasuredHeight() + getTopInset(), 0, MeasureSpec.getSize(heightMeasureSpec)); break; … <CoordinatorLayout fitsSystemWindows> <AppBarLayout fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout>
  148. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final

    int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode != MeasureSpec.EXACTLY && ViewCompat.getFitsSystemWindows(this) && shouldOffsetFirstChild()) { int newHeight = getMeasuredHeight(); switch (heightMode) { case MeasureSpec.AT_MOST: newHeight = MathUtils.clamp( getMeasuredHeight() + getTopInset(), 0, MeasureSpec.getSize(heightMeasureSpec)); break; … <CoordinatorLayout fitsSystemWindows> <AppBarLayout fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout>
  149. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final

    int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode != MeasureSpec.EXACTLY && ViewCompat.getFitsSystemWindows(this) && shouldOffsetFirstChild()) { int newHeight = getMeasuredHeight(); switch (heightMode) { case MeasureSpec.AT_MOST: newHeight = MathUtils.clamp( getMeasuredHeight() + getTopInset(), 0, MeasureSpec.getSize(heightMeasureSpec)); break; … <CoordinatorLayout fitsSystemWindows> <AppBarLayout fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <BottomAppBar /> </CoordinatorLayout> ΋ͱ΋ͱͷAppbarLayoutߴ͞ + status bar ͷߴ͞
  150. ΧελϜView·ͱΊ • CoordinatorLayoutɺAppbarLayoutɺBottomAppbar͸ɺͦΕ ͧΕWindowInsetΛಠࣗͰղऍ͢Δ • ଞʹ΋ɺCollapsingToolbarLayoutɺDrawerLayoutͱ͔΋ͦ͏ • ͜ΕΒͷView͸ɺfitsSystemWindowsΛtrueʹࢦఆ͢Δͱɺ ͍͍ײ͡ʹInsetΛղऍͯ͘͠ΕΔ •

    fitsSystemWindowsࢦఆͯ͠΋ɺফඅ͠ͳ͔ͬͨΓ͢Δ
  151. ଞͷΞϓϦ͔ΒֶͿ

  152. ςʔϚΛߏங͢Δ

  153. APIϨϕϧΛߟྀ͢Δඞཁ͕͋Δ • API23+ • windowLightStatusBar • API27+ • windowLightNavigationBar •

    API29+ • enforceStatusBarContrast • enforceNavigationBarContrast
  154. APIϨϕϧΛߟྀ͢Δඞཁ͕͋Δ • API23+ • windowLightStatusBar • API27+ • windowLightNavigationBar •

    API29+ • enforceStatusBarContrast • enforceNavigationBarContrast 21 22 23 24 25 26 27 28 29
  155. Dev Summit 2019ΞϓϦ https://github.com/google/iosched/pull/333

  156. 21 22 23 24 25 26 27 28 29

  157. 21 22 23 24 25 26 27 28 29

  158. windowLightStatusBar ✗ windowLightNavigationBar ✗ statusBarColor #66000000 navigationBarColor #66000000 API21 22

  159. windowLightStatusBar true windowLightNavigationBar ✗ statusBarColor transparent navigationBarColor #66000000 API23 24

    25 26
  160. windowLightStatusBar true windowLightNavigationBar ✗ statusBarColor transparent navigationBarColor #66000000 API23 24

    25 26
  161. windowLightStatusBar true windowLightNavigationBar true statusBarColor transparent navigationBarColor #B3FFFFFF API27 28

  162. windowLightStatusBar true windowLightNavigationBar true statusBarColor transparent navigationBarColor #B3FFFFFF API27 28

  163. windowLightStatusBar true windowLightNavigationBar true statusBarColor transparent navigationBarColor transparent API29

  164. windowLightStatusBar ✗ / false windowLightNavigationBar ✗ / false statusBarColor transparent

    navigationBarColor #B3000000 API21 ~ 28
  165. windowLightStatusBar ✗ / false windowLightNavigationBar ✗ / false statusBarColor transparent

    navigationBarColor transparent API29
  166. <style name="Base.AppTheme" parent=“…”> <item name="android:windowLightStatusBar" tools:targetApi="m">@bool/ use_light_status</item> <item name="android:windowLightNavigationBar" tools:targetApi=“o_mr1">@bool/use_light_navigation</item>

    </style>
  167. <style name="Base.AppTheme" parent=“…”> <item name="android:windowLightStatusBar" tools:targetApi="m">@bool/ use_light_status</item> <item name="android:windowLightNavigationBar" tools:targetApi=“o_mr1">@bool/use_light_navigation</item>

    </style>
  168. <style name="Base.AppTheme" parent=“…”> <item name="android:windowLightStatusBar" tools:targetApi="m">@bool/ use_light_status</item> <item name="android:windowLightNavigationBar" tools:targetApi=“o_mr1">@bool/use_light_navigation</item>

    </style> <resources> <bool name="use_light_status">true</bool> </resources> values-notnight-v23
  169. <style name="Base.AppTheme" parent=“…”> <item name="android:windowLightStatusBar" tools:targetApi="m">@bool/ use_light_status</item> <item name="android:windowLightNavigationBar" tools:targetApi=“o_mr1">@bool/use_light_navigation</item>

    </style> <resources> <bool name="use_light_status">true</bool> </resources> values-notnight-v23 <resources> <bool name="use_light_status">false</bool> </resources> values
  170. <style name="Base.AppTheme" parent=“…”> <item name="android:windowLightStatusBar" tools:targetApi="m">@bool/ use_light_status</item> <item name="android:windowLightNavigationBar" tools:targetApi=“o_mr1">@bool/use_light_navigation</item>

    </style>
  171. <item name="android:windowLightStatusBar" tools:targetApi="m">@bool/ use_light_status</item> <item name="android:windowLightNavigationBar" tools:targetApi=“o_mr1">@bool/use_light_navigation</item> </style> <resources> <bool

    name="use_light_navigation">true</bool> </resources> values-notnight-v27
  172. <resources> <bool name="use_light_navigation">true</bool> </resources> values-notnight-v27 <resources> <bool name="use_light_navigation">false</bool> </resources> values

    <item name="android:windowLightStatusBar" tools:targetApi="m">@bool/ use_light_status</item> <item name="android:windowLightNavigationBar" tools:targetApi=“o_mr1">@bool/use_light_navigation</item> </style>
  173. <style name="AppTheme" parent="Base.AppTheme"> <item name="android:statusBarColor">@color/status_bar_scrim</item> <item name="android:navigationBarColor">@color/nav_bar_scrim</item> </style>

  174. <style name="AppTheme" parent="Base.AppTheme"> <item name="android:statusBarColor">@color/status_bar_scrim</item> <item name="android:navigationBarColor">@color/nav_bar_scrim</item> </style>

  175. <style name="AppTheme" parent="Base.AppTheme"> <item name="android:statusBarColor">@color/status_bar_scrim</item> <item name="android:navigationBarColor">@color/nav_bar_scrim</item> </style> values-night <color

    name="status_bar_scrim">@color/transparent</color>
  176. <style name="AppTheme" parent="Base.AppTheme"> <item name="android:statusBarColor">@color/status_bar_scrim</item> <item name="android:navigationBarColor">@color/nav_bar_scrim</item> </style> values-night <color

    name="status_bar_scrim">@color/transparent</color> values-notnight-v23 <color name="status_bar_scrim">@color/transparent</color>
  177. <style name="AppTheme" parent="Base.AppTheme"> <item name="android:statusBarColor">@color/status_bar_scrim</item> <item name="android:navigationBarColor">@color/nav_bar_scrim</item> </style> values-night <color

    name="status_bar_scrim">@color/transparent</color> values-notnight-v23 <color name="status_bar_scrim">@color/transparent</color> values <color name="status_bar_scrim">@color/system_ui_scrim_black</color>
  178. values-v29 <style name="AppTheme" parent="Base.AppTheme"> <item name="android:statusBarColor">@color/transparent</item> <item name="android:navigationBarColor">@color/transparent</item> </style>

  179. ςʔϚͰߏங͢Δ·ͱΊ • σΟϨΫτϦΛ૊Έ߹Θͤͯɺߏங͢Δ • values • values-night • values-notnight-v23 •

    values-notnight-v27 • values-v29 • API29͸transparentΛࢦఆ͢Δͱɺ͍͍ײ͡ʹഎܠ৭Λௐ੔ͯ͘͠Ε Δ
  180. StatusBarScrim

  181. None
  182. <com.google.samples.apps.iosched.widget.StatusBarScrim android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorSurface" android:elevation="@dimen/appbar_elevation" android:outlineProvider="none" android:fitsSystemWindows="true" />

  183. class StatusBarScrim override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets { if (insets

    != lastWindowInsets) { lastWindowInsets = insets // Set this view as invisible if there isn't a top inset visibility = if (insets.systemWindowInsetTop > 0) VISIBLE else INVISIBLE // Request a layout to change size requestLayout() } return insets }
  184. class StatusBarScrim override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets { if (insets

    != lastWindowInsets) { lastWindowInsets = insets // Set this view as invisible if there isn't a top inset visibility = if (insets.systemWindowInsetTop > 0) VISIBLE else INVISIBLE // Request a layout to change size requestLayout() } return insets }
  185. class StatusBarScrim override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets { if (insets

    != lastWindowInsets) { lastWindowInsets = insets // Set this view as invisible if there isn't a top inset visibility = if (insets.systemWindowInsetTop > 0) VISIBLE else INVISIBLE // Request a layout to change size requestLayout() } return insets }
  186. <com.google.samples.apps.iosched.widget.StatusBarScrim android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorSurface" android:elevation="@dimen/appbar_elevation" android:outlineProvider="none" android:fitsSystemWindows="true" />

  187. InsetterϥΠϒϥϦ

  188. InsetterϥΠϒϥϦ • WindowInsetsɺSystemUiVisibilityΛ؆୯ʹѻ͏͜ͱ͕ग़དྷΔ void setEdgeToEdgeSystemUiFlags(View view, boolean enabled)

  189. InsetterϥΠϒϥϦ • WindowInsetsɺSystemUiVisibilityΛ؆୯ʹѻ͏͜ͱ͕ग़དྷΔ void setOnApplyInsetsListener(View view, OnApplyInsetsListener listener) public interface

    OnApplyInsetsListener { void onApplyInsets( View view, WindowInsetsCompat insets, ViewState initialState ); }
  190. • WindowInsetsɺSystemUiVisibilityΛ؆୯ʹѻ͏͜ͱ͕ग़དྷΔ void setOnApplyInsetsListener(View view, OnApplyInsetsListener listener) public interface OnApplyInsetsListener

    { void onApplyInsets( View view, WindowInsetsCompat insets, ViewState initialState ); } view.updatePadding( top = view.paddingTop + insets.systemWindowInsetTop )
  191. • WindowInsetsɺSystemUiVisibilityΛ؆୯ʹѻ͏͜ͱ͕ग़དྷΔ void setOnApplyInsetsListener(View view, OnApplyInsetsListener listener) public interface OnApplyInsetsListener

    { void onApplyInsets( View view, WindowInsetsCompat insets, ViewState initialState ); } view.updatePadding( top = view.paddingTop + insets.systemWindowInsetTop ) setOnApplyWindowInsetsListener͸ෳ਺ճίʔϧ͞ΕΔͷͰɺ͜Εͩͱμϝ
  192. • WindowInsetsɺSystemUiVisibilityΛ؆୯ʹѻ͏͜ͱ͕ग़དྷΔ void setOnApplyInsetsListener(View view, OnApplyInsetsListener listener) public interface OnApplyInsetsListener

    { void onApplyInsets( View view, WindowInsetsCompat insets, ViewState initialState ); } view.updatePadding( top = initialState.paddings.top + insets.systemWindowInsetTop )
  193. ࢀߟ • https://github.com/google/iosched • https://medium.com/androiddevelopers/gesture-navigation- going-edge-to-edge-812f62e4e83e • https://medium.com/androiddevelopers/why-would-i-want- to-fitssystemwindows-4e26d9ce1eec •

    https://speakerdeck.com/ytakahashi/xiang-jie-windowinsets • https://satoshun.github.io/2020/01/android- fitssystemwindows/
  194. DroidKaigi 2020 System UIΛίϯτϩʔϧͯ͠ɺ ը໘Λ࠷େݶʹੜ͔ͨ͠ΞϓϦΛߏங͢Δ Sato Shun GitHub: satoshun Twitter:

    @stsn_jp CyberAgent, Inc.