我如何在渲染树的全屏下制作一个可组合的,类似于Dialog可组合的工作方式?例如,当用户单击图像时,它会显示图像的全屏预览,而不会更改当前路线。我可以用position: absolute或position: fixed在CSS中做到这一点,但我如何在Jetpack Compose中做到这一点呢?这可能吗一个解决方案是在树的顶部有一个组合对象,可以从树的其他地方传递另一个组合对象作为参数,但这听起来有点混乱。当然有更好的办法。
Dialog
position: absolute
position: fixed
tcbh2hod1#
据我所知,您希望能够从嵌套层次结构中进行绘制,而不受父约束的限制。我们面临着类似的问题,并研究了Popup、DropDown和Dialog等Composable的实现方式。他们所做的是在Window上添加一个全新的ComposeView。因此,他们基本上是从一个空白的画布开始。通过使其透明,它看起来像对话框/弹出窗口/下拉列表显示在顶部。不幸的是,我们无法找到一个Composable,它为我们提供了将新的ComposeView添加到Window的功能,因此我们复制了相关部分并进行了以下操作。
Popup
DropDown
Window
ComposeView
@Composable fun FullScreen(content: @Composable () -> Unit) { val view = LocalView.current val parentComposition = rememberCompositionContext() val currentContent by rememberUpdatedState(content) val id = rememberSaveable { UUID.randomUUID() } val fullScreenLayout = remember { FullScreenLayout( view, id ).apply { setContent(parentComposition) { currentContent() } } } DisposableEffect(fullScreenLayout) { fullScreenLayout.show() onDispose { fullScreenLayout.dismiss() } } } @SuppressLint("ViewConstructor") private class FullScreenLayout( private val composeView: View, uniqueId: UUID ) : AbstractComposeView(composeView.context) { private val windowManager = composeView.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager private val params = createLayoutParams() override var shouldCreateCompositionOnAttachedToWindow: Boolean = false private set init { id = android.R.id.content ViewTreeLifecycleOwner.set(this, ViewTreeLifecycleOwner.get(composeView)) ViewTreeViewModelStoreOwner.set(this, ViewTreeViewModelStoreOwner.get(composeView)) ViewTreeSavedStateRegistryOwner.set(this, ViewTreeSavedStateRegistryOwner.get(composeView)) setTag(R.id.compose_view_saveable_id_tag, "CustomLayout:$uniqueId") } private var content: @Composable () -> Unit by mutableStateOf({}) @Composable override fun Content() { content() } fun setContent(parent: CompositionContext, content: @Composable () -> Unit) { setParentCompositionContext(parent) this.content = content shouldCreateCompositionOnAttachedToWindow = true } private fun createLayoutParams(): WindowManager.LayoutParams = WindowManager.LayoutParams().apply { type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL token = composeView.applicationWindowToken width = WindowManager.LayoutParams.MATCH_PARENT height = WindowManager.LayoutParams.MATCH_PARENT format = PixelFormat.TRANSLUCENT flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS } fun show() { windowManager.addView(this, params) } fun dismiss() { disposeComposition() ViewTreeLifecycleOwner.set(this, null) windowManager.removeViewImmediate(this) } }
下面是一个例子,你可以使用它
@Composable internal fun Screen() { Column( Modifier .fillMaxSize() .background(Color.Red) ) { Text("Hello World") Box(Modifier.size(100.dp).background(Color.Yellow)) { DeeplyNestedComposable() } } } @Composable fun DeeplyNestedComposable() { var showFullScreenSomething by remember { mutableStateOf(false) } TextButton(onClick = { showFullScreenSomething = true }) { Text("Show full screen content") } if (showFullScreenSomething) { FullScreen { Box( Modifier .fillMaxSize() .background(Color.Green) ) { Text("Full screen text", Modifier.align(Alignment.Center)) TextButton(onClick = { showFullScreenSomething = false }) { Text("Close") } } } } }
黄色框设置了一些约束,这些约束将防止内部的Composables绘制到其边界之外。
z0qdvdin2#
使用对话框组合,我已经能够得到一个适当的全屏组合在任何嵌套之一。它比其他一些答案更快,更容易。
Dialog( onDismissRequest = { /* Do something when back button pressed */ }, properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = false, usePlatformDefaultWidth = false) ){ /* Your full screen content */ }
2sbarzqh3#
如果我理解正确的话,你只是不想去任何地方。像这样的东西。
when (val viewType = viewModel.viewTypeGallery.get()) { is GalleryViewModel.GalleryViewType.Gallery -> { Gallery(viewModel, scope, installId, filePathModifier, fragment, setImageUploadType) } is GalleryViewModel.GalleryViewType.ImageViewer -> { Row(Modifier.fillMaxWidth()) { Image( modifier = Modifier .fillMaxSize(), painter = rememberCoilPainter(viewType.imgUrl), contentScale = ContentScale.Crop, contentDescription = null ) } } }
我只是跟踪视图的类型。在我的例子中,我没有显示一个对话框,我删除了我的整个画廊,并显示一个图像。或者,你可以在你的调用下面有一个if(viewImage)条件,并在它上面放置一个“dialog”。
h9vpoimq4#
在注意到,至少现在,我们没有任何Composable来做“简单的”全屏,我决定实现我的一个,主要是基于@foxtrotuniform6969和@ntoskrnl的想法。此外,我试图尽可能不使用平台相关的功能,然后我认为这是非常适合桌面/Android。您可以在this GitHub repository中检查基本实现。顺便说一下,实现的想法只是:
Composable
FullScreen
.onGloballyPositioned()
Box
compositionLocalOf
我试着在桌面项目中使用这个,似乎是工作,但我还没有在Android中测试。该仓库还包含一个示例。如果可以的话,请随意在存储库中导航并发送一个pull request。:)
wz3gfoph5#
您可以使用Popup来实现以下功能:https://developer.android.com/reference/kotlin/androidx/compose/ui/window/package-summary#Popup(androidx. compose. ui. window. PopupPositionProvider,kotlin.Function0,androidx. compose. ui. window. PopupPropertys,kotlin.Function0)打开包含给定内容的弹出窗口。使用自定义popupPositionProvider定位弹出窗口。
@Composable fun Popup( popupPositionProvider: PopupPositionProvider, onDismissRequest: (() -> Unit)? = null, properties: PopupProperties = PopupProperties(), content: @Composable () -> Unit ): Unit
我做了一个这样的组合。它可以从层次结构中的任何地方打开,并显示一些填充屏幕。这一个显示了一个全屏半透明的背景,然后一些内容:
@Composable private fun MyPopup( modifier: Modifier = Modifier, onDismiss: () -> Unit = {}, content: @Composable () -> Unit, ) { Popup( onDismissRequest = onDismiss, properties = PopupProperties( focusable = true, dismissOnBackPress = true, ), ) { Box( modifier.fillMaxSize(), ) { Canvas(Modifier.fillMaxSize()) { drawRect( color = Color(0xCC7D7D7D), size = size, ) } Row( modifier = Modifier .width(320.dp) .height(300.dp) .background(color = MaterialTheme.colorScheme.onPrimary) .align(Alignment.Center) ) { content() } } } }
像这样使用它:
MyPopup { Text("Hello World") }
5条答案
按热度按时间tcbh2hod1#
据我所知,您希望能够从嵌套层次结构中进行绘制,而不受父约束的限制。
我们面临着类似的问题,并研究了
Popup
、DropDown
和Dialog
等Composable的实现方式。他们所做的是在
Window
上添加一个全新的ComposeView
。因此,他们基本上是从一个空白的画布开始。
通过使其透明,它看起来像对话框/弹出窗口/下拉列表显示在顶部。
不幸的是,我们无法找到一个Composable,它为我们提供了将新的
ComposeView
添加到Window
的功能,因此我们复制了相关部分并进行了以下操作。下面是一个例子,你可以使用它
黄色框设置了一些约束,这些约束将防止内部的Composables绘制到其边界之外。
z0qdvdin2#
使用对话框组合,我已经能够得到一个适当的全屏组合在任何嵌套之一。它比其他一些答案更快,更容易。
2sbarzqh3#
如果我理解正确的话,你只是不想去任何地方。像这样的东西。
我只是跟踪视图的类型。在我的例子中,我没有显示一个对话框,我删除了我的整个画廊,并显示一个图像。
或者,你可以在你的调用下面有一个if(viewImage)条件,并在它上面放置一个“dialog”。
h9vpoimq4#
在注意到,至少现在,我们没有任何
Composable
来做“简单的”全屏,我决定实现我的一个,主要是基于@foxtrotuniform6969和@ntoskrnl的想法。此外,我试图尽可能不使用平台相关的功能,然后我认为这是非常适合桌面/Android。您可以在this GitHub repository中检查基本实现。
顺便说一下,实现的想法只是:
FullScreen
组合对象的目标组合对象树;.onGloballyPositioned()
修改器从与根屏幕尺寸匹配的辅助Box
中删除全屏尺寸/大小;FullScreen
组合存储到适当的compositionLocalOf
示例中(请参见documentation)。我试着在桌面项目中使用这个,似乎是工作,但我还没有在Android中测试。该仓库还包含一个示例。
如果可以的话,请随意在存储库中导航并发送一个pull request。:)
wz3gfoph5#
您可以使用
Popup
来实现以下功能:https://developer.android.com/reference/kotlin/androidx/compose/ui/window/package-summary#Popup(androidx. compose. ui. window. PopupPositionProvider,kotlin.Function0,androidx. compose. ui. window. PopupPropertys,kotlin.Function0)
打开包含给定内容的弹出窗口。使用自定义popupPositionProvider定位弹出窗口。
我做了一个这样的组合。它可以从层次结构中的任何地方打开,并显示一些填充屏幕。这一个显示了一个全屏半透明的背景,然后一些内容:
像这样使用它: