$30 off During Our Annual Pro Sale. View Details »

DroidKaigi 2020: System UIをコントロールして、 画面を最大限に生かし...

Sato Shun
April 02, 2020

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

Sato Shun

April 02, 2020
Tweet

More Decks by Sato Shun

Other Decks in Technology

Transcript

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

    System UIΛίϯτϩʔϧͯ͠ɺ ը໘Λ࠷େݶʹੜ͔ͨ͠ΞϓϦΛߏங͢Δ
  2. • Edge to Edgeͱ͸? • Edge to EdgeΛͳͥ΍Δ͔? • Edge

    to EdgeΛͲͷΑ͏ʹ΍Δ͔ʁ • ΧελϜView͔ΒֶͿ • ଞͷΞϓϦ͔ΒֶͿ
  3. SystemUiVisibility ͱ͸? • System UIʢstatus bar / navigation barʣͷݟͨ໨ɺৼΔ෣͍ Λมߋ͢Δ

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

    • Immersive modeʢ຅ೖϞʔυʣ • status bar / navigation barͷഎޙʹඳը
  5. System UIഎܠ৭ͷ·ͱΊ • API23+: windowLightStatusBarͷ௥Ճ • API27+: windowLightNavigationBarͷ௥Ճ • API29+:

    enforceNavigationBarContrastɺ enforceStatusBarContrastͷ௥Ճ • transparentͷͱ͖ɺ͍͍ײ͡ʹഎܠ৭Λௐ੔ͯ͘͠ΕΔ
  6. <LinearLayout android:fitsSystemWindows> <AppBarLayout android:fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <Button /> </LinearLayout>

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

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

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

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

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

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

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

    ͜ͷfitsSystemWindows͸ແࢹ͞ΕΔ fitsSystemWindows͸σϑΥϧτͰɺ ਂ͞༏ઌ୳ࡧΛ͠ɺݟ͔ͭͬͨ࠷ॳͷΈ༗ޮ
  14. 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() }
  15. 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 }
  16. 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 }
  17. 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 }
  18. 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 }
  19. 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 }
  20. 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; } }
  21. 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; } }
  22. 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; }
  23. 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Λফඅ͢Δ
  24. 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; } }
  25. 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; } }
  26. public void setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener listener) { getListenerInfo().mOnApplyWindowInsetsListener = listener; } Listener͕ొ࿥͞Ε͍ͯΔͱɺ

    fitsSystemWindowsͷಈ࡞ΛΧελϚΠζ͢Δ ͜ͱ͕ग़དྷΔ view.setOnApplyWindowInsetsListener { v, insets -> … }
  27. <CoordinatorLayout fitsSystemWindows> <AppBarLayout fitsSystemWindows> <MaterialToolbar /> </AppBarLayout> <BottomAppBar fitsSystemWindows />

    </CoordinatorLayout> setOnApplyWindowListenerΛ࢖ͬͯɺ σϑΥϧτͷڍಈΛม͍͑ͯΔ ·ͨɺWindowInsets͸ফඅ͍ͯ͠ͳ͍
  28. 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);
  29. 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);
  30. 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);
  31. 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);
  32. final WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) { if (!ObjectsCompat.equals(mLastInsets, insets)) { mLastInsets

    = insets; // Now dispatch to the Behaviors insets = dispatchApplyWindowInsetsToBehaviors(insets); requestLayout(); } return insets; }
  33. <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; }
  34. 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(…)
  35. 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(…)
  36. 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>
  37. 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>
  38. 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; …
  39. 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; …
  40. 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>
  41. 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>
  42. 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>
  43. 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 ͷߴ͞
  44. APIϨϕϧΛߟྀ͢Δඞཁ͕͋Δ • API23+ • windowLightStatusBar • API27+ • windowLightNavigationBar •

    API29+ • enforceStatusBarContrast • enforceNavigationBarContrast 21 22 23 24 25 26 27 28 29
  45. <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
  46. <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>
  47. <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>
  48. ςʔϚͰߏங͢Δ·ͱΊ • σΟϨΫτϦΛ૊Έ߹Θͤͯɺߏங͢Δ • values • values-night • values-notnight-v23 •

    values-notnight-v27 • values-v29 • API29͸transparentΛࢦఆ͢Δͱɺ͍͍ײ͡ʹഎܠ৭Λௐ੔ͯ͘͠Ε Δ
  49. 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 }
  50. 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 }
  51. 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 }
  52. • 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 )
  53. • 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͸ෳ਺ճίʔϧ͞ΕΔͷͰɺ͜Εͩͱμϝ
  54. • 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 )