Jetpack Compose 全屏对话框

Yan*_*ick 4 android android-dialog android-jetpack-compose

我尝试使用以下代码使用 Jetpack Compose 制作全屏对话框:

Dialog(onDismissRequest = { /*TODO*/ }) {
           NewPostDialog()
       }
Run Code Online (Sandbox Code Playgroud)

它最终看起来像这样。如何去除侧面的边距(标记为红色)?

例子

jns*_*jns 11

更新:由于@Nestor佩雷斯提到的,由于撰写1.0.0-RC01可以设置usePlatformDefaultWidthDialogProperties做一个对话填充整个屏幕宽度:

Dialog(
    properties = DialogProperties(usePlatformDefaultWidth = false),
    onDismissRequest...
    ){
      Surface(modifier = Modifier.fillMaxSize()) {
          DialogContent()
      }
    }
Run Code Online (Sandbox Code Playgroud)

ComposeDialog使用,ContextThemeWrapper因此您应该能够使用自定义样式为您的对话框设置主题。

themes.xml

 <style name="Theme.YourApp" parent="Theme.MaterialComponents.Light.NoActionBar">
       //theme content...
        <item name="android:dialogTheme">@style/Theme.DialogFullScreen</item>
    </style>

 <style name="Theme.DialogFullScreen" parent="@style/ThemeOverlay.MaterialComponents.Dialog.Alert">
        <item name="android:windowMinWidthMajor">100%</item>
        <item name="android:windowMinWidthMinor">100%</item>
    </style>
Run Code Online (Sandbox Code Playgroud)

在代码中:

@Composable
fun FullScreenDialog(showDialog:Boolean, onClose:()->Unit) {
    if (showDialog) {
        Dialog(onDismissRequest =  onClose ) {
            Surface(
                modifier = Modifier.fillMaxSize(),
                shape = RoundedCornerShape(16.dp),
                color = Color.LightGray
            ) {
                Box(
                    contentAlignment = Alignment.Center
                ) {
                    Text(modifier = Modifier.align(Alignment.TopCenter),
                        text = "top")
                    Text("center")
                    Text(
                        modifier = Modifier.align(Alignment.BottomCenter),
                        text = "bottom")
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 高度未设置为全屏高度,底部的某些部分显示对话框后面的内容 (6认同)
  • 它有效,谢谢!有没有办法仅将主题应用于特定对话框而不是整个应用程序? (3认同)

doo*_*e89 9

If you want Jetpack compose full screen dialog that covers entire screen, draws under system bars (status and navigation bar), and supports immersive mode, that is officially unsupported yet but I found a work around:

  1. Find Activity and Dialog window (they are 2 different windows) and apply flags from activity window to dialog window, this will allow dialog window to draw under system bars
  2. Update dialog view parent layout params to match full screen
  3. Make system bars transparent so dialog content is visible underneath

Util methods

// Window utils
@Composable
fun getDialogWindow(): Window? = (LocalView.current.parent as? DialogWindowProvider)?.window

@Composable
fun getActivityWindow(): Window? = LocalView.current.context.getActivityWindow()

private tailrec fun Context.getActivityWindow(): Window? =
    when (this) {
        is Activity -> window
        is ContextWrapper -> baseContext.getActivityWindow()
        else -> null
    }
Run Code Online (Sandbox Code Playgroud)

Full screen dialog

@Composable
fun DialogFullScreen(
    onDismissRequest: () -> Unit,
    properties: DialogProperties = DialogProperties(),
    content: @Composable () -> Unit
) {
    Dialog(
        onDismissRequest = onDismissRequest,
        properties = DialogProperties(
            dismissOnBackPress = properties.dismissOnBackPress,
            dismissOnClickOutside = properties.dismissOnClickOutside,
            securePolicy = properties.securePolicy,
            usePlatformDefaultWidth = true, // must be true as a part of work around
            decorFitsSystemWindows = false
        ),
        content = {
            val activityWindow = getActivityWindow()
            val dialogWindow = getDialogWindow()
            val parentView = LocalView.current.parent as View
            SideEffect {
                if (activityWindow != null && dialogWindow != null) {
                    val attributes = WindowManager.LayoutParams()
                    attributes.copyFrom(activityWindow.attributes)
                    attributes.type = dialogWindow.attributes.type
                    dialogWindow.attributes = attributes
                    parentView.layoutParams = FrameLayout.LayoutParams(activityWindow.decorView.width, activityWindow.decorView.height)
                }
            }

            val systemUiController = rememberSystemUiController(getActivityWindow())
            val dialogSystemUiController = rememberSystemUiController(getDialogWindow())

            DisposableEffect(Unit) {
                systemUiController.setSystemBarsColor(color = Color.Transparent)
                dialogSystemUiController.setSystemBarsColor(color = Color.Transparent)

                onDispose {
                    systemUiController.setSystemBarsColor(color = previousSystemBarsColor)
                    dialogSystemUiController.setSystemBarsColor(color = previousSystemBarsColor)
                }
            }

            // If you need Immersive mode
            val isImmersive = true
            DisposableEffect(isImmersive) {
                systemUiController.isSystemBarsVisible = !isImmersive
                dialogSystemUiController.isSystemBarsVisible = !isImmersive

                onDispose {
                    systemUiController.isSystemBarsVisible = true
                    dialogSystemUiController.isSystemBarsVisible = true
                }
            }

            Surface(modifier = Modifier.fillMaxSize(), color = Color.Transparent) {
                content()
            }
        }
    )
}
Run Code Online (Sandbox Code Playgroud)

Full screen dialog with navigation

fun NavGraphBuilder.dialogFullScreen(
    route: String,
    arguments: List<NamedNavArgument> = emptyList(),
    deepLinks: List<NavDeepLink> = emptyList(),
    dialogProperties: DialogProperties = DialogProperties(),
    content: @Composable (NavBackStackEntry) -> Unit
) {
    dialog(
        route = route,
        arguments = arguments,
        deepLinks = deepLinks,
        dialogProperties = DialogProperties(
            dismissOnBackPress = dialogProperties.dismissOnBackPress,
            dismissOnClickOutside = dialogProperties.dismissOnClickOutside,
            securePolicy = dialogProperties.securePolicy,
            usePlatformDefaultWidth = true, // must be true as a part of work around
            decorFitsSystemWindows = false
        ),
        content = {
            val activityWindow = getActivityWindow()
            val dialogWindow = getDialogWindow()
            val parentView = LocalView.current.parent as View
            SideEffect {
                if (activityWindow != null && dialogWindow != null) {
                    val attributes = WindowManager.LayoutParams()
                    attributes.copyFrom(activityWindow.attributes)
                    attributes.type = dialogWindow.attributes.type
                    dialogWindow.attributes = attributes
                    parentView.layoutParams = FrameLayout.LayoutParams(activityWindow.decorView.width, activityWindow.decorView.height)
                }
            }

            val systemUiController = rememberSystemUiController(getActivityWindow())
            val dialogSystemUiController = rememberSystemUiController(getDialogWindow())

            DisposableEffect(Unit) {
                systemUiController.setSystemBarsColor(color = Color.Transparent)
                dialogSystemUiController.setSystemBarsColor(color = Color.Transparent)

                onDispose {
                    systemUiController.setSystemBarsColor(color = previousSystemBarsColor)
                    dialogSystemUiController.setSystemBarsColor(color = previousSystemBarsColor)
                }
            }

            // If you need Immersive mode
            val isImmersive = true
            DisposableEffect(isImmersive) {
                systemUiController.isSystemBarsVisible = !isImmersive
                dialogSystemUiController.isSystemBarsVisible = !isImmersive

                onDispose {
                    systemUiController.isSystemBarsVisible = true
                    dialogSystemUiController.isSystemBarsVisible = true
                }
            }
            
            Surface(modifier = Modifier.fillMaxSize(), color = Color.Transparent) {
                content(it)
            }
        }
    )
}
Run Code Online (Sandbox Code Playgroud)

  • 这个对我有用 !谢谢 (2认同)

Nes*_*rez 5

jns 的解决方案对我来说效果不太好,如果有人仍在寻找,我在这里留下另一个解决方案:

将主题实现为 jns 答案:

<style name="Theme.Outlay" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    ...
    <!-- Customize your theme here. -->
    <item name="android:dialogTheme">@style/Theme.DialogFullScreen</item    >
</style>

<style name="Theme.DialogFullScreen" parent="@style/ThemeOverlay.MaterialComponents.Dialog.Alert">
    <item name="android:windowMinWidthMajor">100%</item>
    <item name="android:windowMinWidthMinor">100%</item>
</style>
Run Code Online (Sandbox Code Playgroud)

为对话框创建一个脚手架并在对话框属性上添加实验属性“usePlatformDefaultWidth = false”:

Dialog(
    onDismissRequest = onBackPressed,
    properties = DialogProperties(
        usePlatformDefaultWidth = false
    )
) {
    Scaffold(topBar = { TopBar(onBackPressed = onBackPressed) }) {
        Content()
    }
}
Run Code Online (Sandbox Code Playgroud)