Android (API level >= 30) で完全に透明なSystemBar (NavigationBar + StatusBar)を指定する方法

公開日: 2022年01月27日最終更新日: 2022年01月28日

transparent-system-bar.png

SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATIONはAPI level 30でdeprecatedになっており、setDecorFitsSystemWindowsを使うことが推奨されています。

3 button navigationの場合に、SystemBar(NavigationBar)を完全に透明にする設定を少し調べることになったため、メモとして記載します。

テーマの設定

テーマでStatusBarとNavigationBarの色を透明に指定します。
android:windowDrawsSystemBarBackgroundsがfalseになっているとSystemBarが透過しないので注意してください。
Material系のテーマではandroid:windowDrawsSystemBarBackgroundsがtrueになってるため記載不要ですが、必要に応じてテーマに記載してください。

<resources>
    <!-- Base application theme. -->
    <style name="Theme.TransparentSystemBarStudy" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
        <item name="android:statusBarColor">@android:color/transparent</item>
        <item name="android:navigationBarColor">@android:color/transparent</item>
    </style>
</resources>

コード上で以下のようにしても同じです。

window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(Color.TRANSPARENT);

AndroidManifest.xmlのapplicationタグ内に以下を追記して、作成したテーマを適用します。

  <application
        ...
        android:theme="@style/Theme.TransparentSystemBarStudy">

onCreateでの設定

次に、アプリの描画領域をSystemBarの裏まで広げます。 setDecorFitsSystemWindowsをfalseにするとアプリの描画範囲が画面全体になります。

setStatusBarContrastEnforcedsetNavigationBarContrastEnforcedをfalseにすることで、アイコンと背景のコントラストが不十分な場合でも背景にscrimを表示しないようにします。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            window.setDecorFitsSystemWindows(false)
            window.setStatusBarContrastEnforced(false)
            window.setNavigationBarContrastEnforced(false)
        }
    }

ここの処理を十分に確認できていないのですが、これをしないと背景とのコントラストが十分でもScrimが表示されてしまい、NavigationBarが完全に透明にならない気がします。StatusBarは透明になるんですが。
また、3 button navigationの場合、setContentViewの後で呼ばないとScrimが表示されてしまうところも少し気になります。(時間があれば後で調べよう・・・)

NavigationBarのscrimは以下の画像のような黒い半透明の背景です。

scrim-navigation-bar.png

レイアウトの設定

描画領域がSystemBarの裏まで広がっているのでそのままレイアウトすると、GUIがStatusBarやNavigationBarの裏に入り込んでしまいます。 そこでレイアウトにandroid:fitsSystemWindows="true"をLayoutGroupに指定することで、SystemBarのある領域を避けてGUIを配置することができます。

これを入れるとSystemBarの領域を避けた領域にレイアウトします。 以下の例では全体にImageViewを表示し、TextViewはSystemBarより内側に配置するようにしています。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:contentDescription="image"
        android:scaleType="centerCrop"
        android:src="@drawable/image" />

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#8888FF88"
            android:text="Hello World!"
            android:textSize="64sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#888888FF"
            android:text="Hello World!"
            android:textSize="64sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

参考