You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose中Autocomplete TextField展开致同行视图下移的修复

修复Jetpack Compose中AutoComplete TextField下拉面板导致同行视图偏移的问题

我在Jetpack Compose中实现了一个AutoComplete TextField,并将其放置在Row布局的两个视图之间。当自动补全下拉面板展开时,同行的其他视图会向下偏移,如何修复该问题?

原实现代码

@Composable
fun AutoCompleteTextField(
    modifier: Modifier = Modifier,
    list: List<Product>,
    searchTerm: TextFieldValue,
    updateSearchTerm: (TextFieldValue) -> Unit,
) {

    var product by remember {
        mutableStateOf("")
    }

    var heightTextFields by remember {
        mutableStateOf(55.dp)
    }

    var textFieldSize by remember {
        mutableStateOf(Size.Zero)
    }

    var expanded by remember {
        mutableStateOf(false)
    }

    val interactionSource = remember {
        MutableInteractionSource()
    }

    Column(
        modifier = modifier
            .width(200.dp)
            .clickable(
                interactionSource = interactionSource,
                indication = null,
                onClick = {
                    expanded = false
                }
            )
    ) {

        Column(modifier = modifier.fillMaxWidth()) {
            Row(
                modifier = modifier
                    .border(BorderStroke(width = 1.dp, color = Color.LightGray))
                    .fillMaxWidth(),
                verticalAlignment = Alignment.CenterVertically
            ) {

                IconButton(onClick = {product = "" }) {
                    Icon(
                        imageVector = Icons.Filled.Add,
                        contentDescription = "Add",
                        modifier = modifier
                            .padding(4.dp)
                            .align(Alignment.CenterVertically)
                    )
                }
                TextField(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(heightTextFields)
                        .onGloballyPositioned { coordinates ->
                            textFieldSize = coordinates.size.toSize()
                        },
                    value = product,
                    onValueChange = {
                        product = it
                        expanded = true
                    },
                    colors = TextFieldDefaults.textFieldColors(
                        backgroundColor = Color.Transparent,
                        focusedIndicatorColor = Color.Transparent,
                        unfocusedIndicatorColor = Color.Transparent,
                        cursorColor = Color.Black
                    ),
                    textStyle = TextStyle(
                        fontFamily = shabnamFontFamily,
                        color = MaterialTheme.colorScheme.secondary,
                        fontWeight = FontWeight.Bold,
                        fontSize = 14.sp,
                    ),
                    keyboardOptions = KeyboardOptions(
                        keyboardType = KeyboardType.Text,
                        imeAction = ImeAction.Done
                    ),
                    singleLine = true,
                )
            }

            AnimatedVisibility(visible = expanded) {
                Card(
                    modifier = modifier
                        .padding(horizontal = 4.dp)
                        .width(textFieldSize.width.dp),
                    elevation = 10.dp,
                    shape = RoundedCornerShape(bottomStart = 4.dp, bottomEnd = 4.dp)
                ) {

                    LazyColumn(
                        modifier = Modifier.heightIn(max = 150.dp),
                    ) {

                        if (product.isNotEmpty()) {
                            items(
                                list.filter {
                                    it.productName.lowercase()
                                        .contains(product.lowercase())
                                }
                            ) { item ->
                                ProductItems(title = item.productName) { title ->
                                    product = title
                                    expanded = false
                                }
                            }
                        } else {
                            items(
                                items =
                                list
                            ) { item ->
                                ProductItems(title = item.productName) { title ->
                                    product = title
                                    expanded = false
                                }
                            }
                        }

                    }

                }
            }

        }

    }

}

问题原因

当前的下拉面板通过AnimatedVisibility嵌套在Column布局中,属于布局流的一部分。当面板展开时,会增加所在Column的高度,导致整个AutoCompleteTextField组件的高度被撑开,进而推动所在Row中的其他视图向下偏移。

修复方案

使用Jetpack Compose的Popup组件承载下拉列表,让下拉面板悬浮在原有布局上方,脱离布局流,这样就不会影响同行视图的位置。

修改后的代码

@Composable
fun AutoCompleteTextField(
    modifier: Modifier = Modifier,
    list: List<Product>,
    searchTerm: TextFieldValue,
    updateSearchTerm: (TextFieldValue) -> Unit,
) {

    var product by remember {
        mutableStateOf("")
    }

    var heightTextFields by remember {
        mutableStateOf(55.dp)
    }

    var textFieldBounds by remember {
        mutableStateOf(Rect.Zero)
    }

    var expanded by remember {
        mutableStateOf(false)
    }

    val interactionSource = remember {
        MutableInteractionSource()
    }

    Box(
        modifier = modifier
            .width(200.dp)
            .clickable(
                interactionSource = interactionSource,
                indication = null,
                onClick = {
                    expanded = false
                }
            )
    ) {
        Row(
            modifier = modifier
                .border(BorderStroke(width = 1.dp, color = Color.LightGray))
                .fillMaxWidth()
                .onGloballyPositioned { coordinates ->
                    // 获取TextField所在Row的全局位置信息
                    textFieldBounds = coordinates.boundsInWindow()
                },
            verticalAlignment = Alignment.CenterVertically
        ) {

            IconButton(onClick = { product = "" }) {
                Icon(
                    imageVector = Icons.Filled.Add,
                    contentDescription = "Add",
                    modifier = modifier
                        .padding(4.dp)
                        .align(Alignment.CenterVertically)
                )
            }
            TextField(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(heightTextFields),
                value = product,
                onValueChange = {
                    product = it
                    expanded = true
                },
                colors = TextFieldDefaults.textFieldColors(
                    backgroundColor = Color.Transparent,
                    focusedIndicatorColor = Color.Transparent,
                    unfocusedIndicatorColor = Color.Transparent,
                    cursorColor = Color.Black
                ),
                textStyle = TextStyle(
                    fontFamily = shabnamFontFamily,
                    color = MaterialTheme.colorScheme.secondary,
                    fontWeight = FontWeight.Bold,
                    fontSize = 14.sp,
                ),
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Text,
                    imeAction = ImeAction.Done
                ),
                singleLine = true,
            )
        }

        // 使用Popup承载下拉列表
        Popup(
            onDismissRequest = { expanded = false },
            // 设置Popup的位置为TextField下方
            offset = IntOffset(
                x = textFieldBounds.left.roundToInt(),
                y = textFieldBounds.bottom.roundToInt()
            )
        ) {
            if (expanded) {
                Card(
                    modifier = Modifier
                        .padding(horizontal = 4.dp)
                        .width(textFieldBounds.width.dp),
                    elevation = 10.dp,
                    shape = RoundedCornerShape(bottomStart = 4.dp, bottomEnd = 4.dp)
                ) {
                    LazyColumn(
                        modifier = Modifier.heightIn(max = 150.dp),
                    ) {
                        val filteredList = if (product.isNotEmpty()) {
                            list.filter {
                                it.productName.lowercase()
                                    .contains(product.lowercase())
                            }
                        } else {
                            list
                        }

                        items(filteredList) { item ->
                            ProductItems(title = item.productName) { title ->
                                product = title
                                expanded = false
                            }
                        }
                    }
                }
            }
        }
    }
}

关键修改点

  • 将外层的Column替换为Box,方便Popup定位
  • 使用onGloballyPositioned获取TextField所在Row的全局边界信息,用于Popup的位置计算
  • 用Popup替代AnimatedVisibility承载下拉面板,让面板悬浮在布局上方,不占用布局流空间
  • 调整Popup的偏移量,使其精准显示在TextField下方

内容的提问来源于stack exchange,提问作者Saeed Noshadi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 16:50:24