こんにちは。Android アプリ開発担当の nagayama(@nagayan_dev)です。
今回は Jetpack Compose で横向きフルスクリーン表示の対応をしたため、その内容をお伝えします。
対応方針
今回は WebView を、横画面 かつ フルスクリーン で表示をしたいと思います。WebView の実装は SamplePage
の Composable で実装しており、詳細は割愛します。
また一時的な表示とするため、ダイアログで横画面・フルスクリーンの表示にします。そのダイアログが終了すると、画面は元に戻る仕様にします。
実装
ダイアログを表示する
Dialog を用いて、ダイアログを実装します。fullScreen
フラグを remember
で保持し、表示切り替えができるようにします。
var fullScreen by remember { mutableStateOf(true) } if (fullScreen) { Dialog( onDismissRequest = { // ダイアログがキャンセルになった時の処理 fullScreen = false }, content = { SamplePage( modifier = Modifier .fillMaxSize() ) } ) }
縦横切り替え
続けて画面を縦から横に切り替える処理を実装します。下記のように、画面の縦横を切り替える Composable を作成します。
Fragment 等で縦横切り替えを行う処理と同様に、Activity のメソッドで切り替えを行います。 Activity を参照するため、 Context から探すメソッドを作成します。
今回は一時的な横画面表示であるため、DisposableEffect の onDispose
で Composable が破棄された時に元に戻すようにします。
@Composable private fun ChangeOrientationLandscape() { val context = LocalContext.current if (context.resources.configuration.orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) return DisposableEffect(context) { val activity = context.findActivity() ?: return@DisposableEffect onDispose {} activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE onDispose { activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT } } } private fun Context.findActivity(): Activity? { return when (this) { is Activity -> this is ContextWrapper -> baseContext.findActivity() else -> null } }
Dialog(
〜省略〜
content = {
// 横画面切り替え
ChangeOrientationLandscape()
SamplePage(
modifier = Modifier
.fillMaxSize()
)
}
)
フルスクリーン
フルスクリーン表示の実装を行います。
まず、Dialog の properties
パラメータに DialogProperties のインスタンスを指定し、decorFitsSystemWindows
を false に設定します。これにより Dialog の横幅を変更できるようになります。
次に、ダイアログで表示している Window サイズをリサイズし、画面全体で表示するよう LayoutParams を操作します。先ほどと同じ処理から Activity を参照し、Dialog の window
と親 View にコピーします。
ここまで行うと、ナビゲーションバーにダイアログが被って表示されてしまいます。これを回避するため、 WindowInsets からナビゲーションバーの高さを取得し、その分の padding
を設定します。ジェスチャーナビゲーションを設定している場合はこの高さが 0 になるため、問題なく表示されます。
Dialog( 〜省略〜 properties = DialogProperties( decorFitsSystemWindows = false ), content = { ChangeOrientationLandscape() // フルスクリーン val activityWindow = LocalView.current.context.findActivity()?.window val dialogWindow = (LocalView.current.parent as? DialogWindowProvider)?.window val parentView = LocalView.current.parent as View SideEffect { if (activityWindow != null && dialogWindow != null) { val attributes = WindowManager.LayoutParams().also { it.copyFrom(activityWindow.attributes) it.type = dialogWindow.attributes.type } dialogWindow.attributes = attributes parentView.layoutParams = FrameLayout.LayoutParams(activityWindow.decorView.width, activityWindow.decorView.height) } } // 全画面にした時 Navigation Bar に被って表示されてしまうため、そのサイズ分 padding を設定します val navigationPadding = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(LayoutDirection.Ltr) SamplePage( modifier = Modifier .fillMaxSize() .padding(end = navigationPadding) ) } )
ステータスバー表示切り替え
最後の仕上げです。フルスクリーンで表示された WebView の上にステータスバーのアイコン等が表示されます。フルスクリーン時にステータスバーを非表示にするため、再度 Activity を取得して Window クラスに処理を行います。
API レベル 30 以上の場合は、WindowInsetsController を取得し、show
/ hide
でステータスバーを指定して表示を切り替えます。 30 未満の場合は、Window クラスの setFlags
/ clearFlags
でステータスバーの Flag を追加・削除をして切り替えを行います。
@Composable fun ChangeStatusBarVisibility(isShowStatusBar: Boolean) { val activityWindow = LocalView.current.context.findActivity()?.window ?: return if (isShowStatusBar) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { activityWindow.decorView.windowInsetsController?.show(android.view.WindowInsets.Type.statusBars()) } else { activityWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) } } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { activityWindow.decorView.windowInsetsController?.hide(android.view.WindowInsets.Type.statusBars()) } else { activityWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN) } } }
@Composable fun FullScreenSample() { var fullScreen by remember { mutableStateOf(false) } if (fullScreen) { // フルスクリーン表示 Dialog(〜省略〜) } else { // 通常表示 〜省略〜 } ChangeStatusBarVisibility(!fullScreen) }
以上で横画面フルスクリーン表示ができました。
まとめ
Jetpack Compose の横向きフルスクリーン表示についてまとめました。やることが多く複雑な処理が多いですが、処理を分割して整理しながら実装できるのは Compose の大きなメリットであると思います。これからも Jetpack Compose ライフを楽しみましょう。