如何在Jetpack Compose中实现带特定聚焦/失焦样式的自定义TextField
Jetpack Compose 自定义带悬浮提示的TextField组件实现方案
组件样式说明
- 失焦状态:提示文字位于输入框内部,边框为浅灰色细边框,无高亮效果
- 聚焦状态:提示文字缩小上浮到输入框顶部边框位置,边框切换为主题高亮色,宽度加粗
核心实现思路
选择BasicTextField作为底层基础组件,相比封装度高的Material TextField,自定义样式的自由度更高,核心需要监听两个状态:输入框的焦点状态、输入文本的内容状态,配合属性动画实现提示文字的过渡效果。
具体实现步骤
- 定义组件入参,预留自定义配置入口,支持传入提示文本、失焦边框颜色、聚焦边框颜色、文字样式、是否可编辑、文本变更回调等参数,适配不同业务场景
- 监听焦点状态,通过
FocusRequester配合Modifier.onFocusChanged捕获输入框的焦点变更,更新当前是否聚焦的状态变量 - 绘制容器与边框,外层容器预留顶部padding给上浮的提示文字,根据焦点状态动态切换边框的颜色和宽度,圆角可以根据需求自定义
- 实现提示文字的悬浮动画,用
animateDpAsState实现提示文字的Y轴位移动画,animateFloatAsState实现文字大小的缩放动画:当输入框聚焦或者文本不为空时,提示文字上浮到顶部边框、缩小字号;失焦且文本为空时回到输入框内部、恢复原字号 - 嵌入输入文本区域,调整
BasicTextField的内边距,避免输入文本和上浮的提示文字重叠,配置键盘类型、单行/多行等基础属性
示例代码
@Composable fun CustomOutlinedTextField( hint: String, text: String, onTextChange: (String) -> Unit, modifier: Modifier = Modifier, focusedBorderColor: Color = MaterialTheme.colors.primary, unfocusedBorderColor: Color = Color.LightGray, borderWidth: Dp = 1.dp, focusedBorderWidth: Dp = 2.dp, textStyle: TextStyle = LocalTextStyle.current, hintStyle: TextStyle = textStyle.copy(color = Color.Gray), cornerRadius: Dp = 8.dp ) { val focusRequester = remember { FocusRequester() } var isFocused by remember { mutableStateOf(false) } // 提示文字动画参数 val hintOffsetY by animateDpAsState( targetValue = if (isFocused || text.isNotEmpty()) (-12).dp else 0.dp, label = "hintOffsetY" ) val hintScale by animateFloatAsState( targetValue = if (isFocused || text.isNotEmpty()) 0.8f else 1f, label = "hintScale" ) val currentBorderColor by animateColorAsState( targetValue = if (isFocused) focusedBorderColor else unfocusedBorderColor, label = "borderColor" ) val currentBorderWidth by animateDpAsState( targetValue = if (isFocused) focusedBorderWidth else borderWidth, label = "borderWidth" ) Box( modifier = modifier .fillMaxWidth() .border( width = currentBorderWidth, color = currentBorderColor, shape = RoundedCornerShape(cornerRadius) ) .padding(horizontal = 16.dp, vertical = 12.dp) .focusRequester(focusRequester) .onFocusChanged { isFocused = it.isFocused } .clickable { focusRequester.requestFocus() } ) { // 提示文字:背景和页面背景一致,遮盖边框实现嵌入效果 Text( text = hint, style = hintStyle, modifier = Modifier .align(Alignment.CenterStart) .graphicsLayer { translationY = hintOffsetY.toPx() scaleX = hintScale scaleY = hintScale } .background(MaterialTheme.colors.surface) .padding(horizontal = 4.dp) ) // 输入文本区域 BasicTextField( value = text, onValueChange = onTextChange, modifier = Modifier .fillMaxWidth() .padding(top = 8.dp), textStyle = textStyle, singleLine = true ) } }
使用示例
@Composable fun UsageDemo() { var inputText by remember { mutableStateOf("") } CustomOutlinedTextField( hint = "请输入内容", text = inputText, onTextChange = { inputText = it }, modifier = Modifier.padding(16.dp) ) }
内容的提问来源于stack exchange,提问作者Saeed Noshadi
相关产品推荐
相关产品推荐

