Android (API level >= 30) で完全に透明なSystemBar (NavigationBar + StatusBar)を指定する方法
公開日: 2022年01月27日最終更新日: 2022年01月28日
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にするとアプリの描画範囲が画面全体になります。
setStatusBarContrastEnforcedとsetNavigationBarContrastEnforcedを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は以下の画像のような黒い半透明の背景です。
レイアウトの設定
描画領域が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>