You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Jetpack Compose Dialog中DropdownMenu忽略锚点,错误显示在窗口左上角/屏幕区域

Jetpack Compose Dialog中DropdownMenu忽略锚点,错误显示在窗口左上角/屏幕区域

我太懂你这个痛点了!在Dialog里用DropdownMenu的时候,菜单总是乱跑,不跟着触发它的元素走,确实挺闹心的。其实问题出在Dialog本身是一个Popup层级的容器,默认的DropdownMenu会把整个屏幕作为坐标原点来计算位置,完全忽略了Dialog内部的布局结构。

别担心,我给你两个关键的修改点,就能让菜单乖乖锚定到对应的触发元素上:

解决思路

我们需要让DropdownMenu相对于它所在的Box容器(也就是触发元素的父布局)来定位,而不是整个屏幕。通过给DropdownMenu添加Modifier.align()修饰符,就能指定它在Box里的位置,从而锚定到触发元素的下方。

修改后的完整代码

@Composable
fun CustomComposeDialog(onDismiss: () -> Unit) {
    var textInput1 by remember { mutableStateOf("") }
    var textInput2 by remember { mutableStateOf("") }
    
    // Independent states for two different menus
    var headerMenuExpanded by remember { mutableStateOf(false) }
    val textFieldMenuExpanded = textInput1.length > 3

    Dialog(onDismissRequest = onDismiss) {
        // The container for the dialog content
        Surface(
            shape = RoundedCornerShape(16.dp),
            color = MaterialTheme.colorScheme.surface,
            modifier = Modifier.fillMaxWidth().padding(16.dp)
        ) {
            Column(
                modifier = Modifier.padding(20.dp),
                verticalArrangement = Arrangement.spacedBy(16.dp)
            ) {
                Text(
                    text = "Custom Entry",
                    style = MaterialTheme.typography.headlineSmall
                )

                // --- 1. Header with Icon & its Menu ---
                Box {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        verticalAlignment = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.SpaceBetween
                    ) {
                        Text("Category Options")
                        IconButton(onClick = { headerMenuExpanded = true }) {
                            Icon(Icons.Default.ArrowDropDown, contentDescription = "Open category menu")
                        }
                    }
                    
                    DropdownMenu(
                        expanded = headerMenuExpanded,
                        onDismissRequest = { headerMenuExpanded = false },
                        // 让菜单对齐Box的右上角,对应IconButton的位置
                        modifier = Modifier.align(Alignment.TopEnd)
                    ) {
                        DropdownMenuItem(text = { Text("Option A") }, onClick = { headerMenuExpanded = false })
                        DropdownMenuItem(text = { Text("Option B") }, onClick = { headerMenuExpanded = false })
                    }
                }

                // --- 2. First Text Field & its Anchored Menu ---
                Box {
                    OutlinedTextField(
                        value = textInput1,
                        onValueChange = { textInput1 = it },
                        label = { Text("Search or Type...") },
                        modifier = Modifier.fillMaxWidth()
                    )

                    // This menu anchors specifically to the TextField Box
                    DropdownMenu(
                        expanded = textFieldMenuExpanded,
                        onDismissRequest = { /* Controlled by text length */ },
                        // Focusable false allows the user to keep typing while menu is open
                        properties = androidx.compose.ui.window.PopupProperties(focusable = false),
                        // 让菜单对齐Box的左下角,对应TextField的下方
                        modifier = Modifier.align(Alignment.BottomStart)
                    ) {
                        val suggestions = listOf("Result 1", "Result 2", "Result 3")
                        suggestions.forEach { suggestion ->
                            DropdownMenuItem(
                                text = { Text(suggestion) },
                                onClick = { 
                                    textInput1 = suggestion 
                                    // Note: In a real app, you'd reset a flag here to hide it
                                }
                            )
                        }
                    }
                }

                // --- 3. Second Text Field ---
                OutlinedTextField(
                    value = textInput2,
                    onValueChange = { textInput2 = it },
                    label = { Text("Additional Notes") },
                    modifier = Modifier.fillMaxWidth()
                )

                // Close Button
                TextButton(
                    onClick = onDismiss,
                    modifier = Modifier.align(Alignment.End)
                ) {
                    Text("Close")
                }
            }
        }
    }
}

关键修改说明

  • Header菜单:给DropdownMenu添加Modifier.align(Alignment.TopEnd),让它对齐Box的右上角,正好在下拉图标的正下方。
  • TextField菜单:添加Modifier.align(Alignment.BottomStart),让它对齐Box的左下角,完美贴合TextField的底部。

这样修改后,菜单就会乖乖跟着触发元素走,不会再跑到屏幕左上角啦!

备注:内容来源于stack exchange,提问作者Wisal Muhammad

火山引擎 最新活动