【问题标题】:Jetpack Compose: Make full-screen (absolutely positioned) componentJetpack Compose:制作全屏(绝对定位)组件
【发布时间】:2021-07-20 19:20:13
【问题描述】:

如何在渲染树的全屏深处制作可组合,类似于 Dialog 可组合的工作方式?

例如,当用户单击图像时,它会显示图像的全屏预览,而不会更改当前路线。

我可以在 CSS 中使用 position: absoluteposition: fixed 执行此操作,但我将如何在 Jetpack Compose 中执行此操作?有没有可能?

一种解决方案是在树的顶部有一个可组合项,可以将另一个可组合项作为参数从树中的其他位置传递,但这听起来有点混乱。肯定有更好的方法。

【问题讨论】:

  • 能否请您澄清一下,“更改当前路线”是什么意思?
  • @SandroKakhetelidze 我所说的“不改变当前路线”是指不导航到新屏幕,即使用Jetpack Compose Navigation
  • Dialog composable 只是在新窗口中显示一个以可组合视图作为其内容的对话框。您想要实现的是在布局边界之外进行绘制的能力。它通过clipToPadding="false"clipChildren="false" 属性在标准的android UI 工具包中可用。但据我了解,您希望使树深处的一些随机视图能够在其边界之外绘制......我认为在 Android 中不使所有布局都“clipChildren = false”是不可能的。 Compose 也是如此。
  • 我认为有一些全局“渲染”需要可组合并在顶部绘制,这实际上是一个不错的主意。它可以是一个对象,您可以通过CompositionLocal 使所有层次结构都可以访问它。
  • 它将渲染为主树顶部的另一层。你甚至可以在彼此之上有许多层。但是你必须确保你可以管理它的状态(有点像窗口管理器用窗口层做的)。

标签: android kotlin android-jetpack android-jetpack-compose


【解决方案1】:

据我所知,您希望能够从嵌套层次结构中绘制而不受父约束的限制。

我们遇到了类似的问题,并查看了诸如 PopupDropDownDialog 等 Composable 的实现方式。

他们所做的是在Window 中添加一个全新的ComposeView
因此,它们基本上是从空白画布开始的。
通过使其透明,看起来 Dialog/Popup/DropDown 出现在顶部。

不幸的是,我们找不到可让我们将新的 ComposeView 添加到 Window 的功能的 Composable,因此我们复制了相关部分并进行了以下操作。

@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")
                }
            }
        }
    }
}

黄色框设置了一些约束,这将阻止可组合项从内部绘制到其边界之外。

【讨论】:

  • 我一直在尝试使用您的解决方案来创建我自己的自定义对话框,该对话框具有可配置的稀松布颜色。我能够创建它,但是如果我单击屏幕上任何不是“关闭”按钮的位置,全屏将被冻结并且无法再关闭它。你知道为什么吗? @timr
【解决方案2】:

使用可组合的对话框,我已经能够在任何嵌套的组合中获得适当的全屏可组合。它比其他一些答案更快更容易。

Dialog(
    onDismissRequest = { /* Do something when back button pressed */ },
    properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = false, usePlatformDefaultWidth = false)
){
    /* Your full screen content */
}

【讨论】:

  • 这不是全屏,因为状态栏仍然可见。
【解决方案3】:

如果我理解正确,您只是不想在任何地方导航。我是这样的。

 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) 条件,并在其上方添加“对话框”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-06-29
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 2022-09-23
    • 2022-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多