如何阻止Material3 TimeInput始终保持焦点状态?
问题
我正在实现一个自定义Dialog,将Material3的DatePicker与TimeInput整合在一起,让用户可一次性选择开始日期和时间。界面显示正常,但TimeInput始终会聚焦在小时或分钟输入框上,且无法取消该焦点:
- 打开对话框时,小时输入框会被选中,键盘自动弹出
- 点击TimePicker外部或关闭键盘都无法清除焦点
查了相关文档没找到关于这种焦点行为的说明,这看起来像是个Bug,想问下是否遗漏了阻止该行为的方法?
自定义DateTimeDialog代码
@Suppress("LongParameterList") @Composable fun DateTimeDialog( datePickerState: DatePickerState, timeInputState: TimePickerState, pickerType: CalendarPickerType, title: @Composable () -> Unit = { DefaultTitle() }, headline: @Composable () -> Unit = { DefaultHeadline(state = datePickerState) }, onDismissRequest: () -> Unit, onConfirmRequest: (DateTimeData) -> Unit, dismissButtonText: String = stringResource(id = R.string.cancel), confirmButtonText: String = stringResource(id = R.string.save) ) { Dialog(onDismissRequest = onDismissRequest) { Column( modifier = Modifier .background( Theme.colors.backgroundPrimary, RoundedCornerShape(Theme.spacing.spacing150) ), horizontalAlignment = Alignment.CenterHorizontally ) { DatePicker( modifier = Modifier .clip(RoundedCornerShape(Theme.spacing.spacing150)), title = title, state = datePickerState, headline = headline, dateValidator = { calendarValidator(pickerType, it) }, showModeToggle = false, colors = datePickerColors() ) TimeInput( state = timeInputState, colors = timePickerColors() ) BottomButtons( confirmButtonEnabled = datePickerState.selectedDateMillis != null, onDismissRequest = onDismissRequest, onConfirmRequest = { datePickerState.selectedDateMillis?.let { onConfirmRequest( DateTimeData( dateEpochMilli = it, timeHour = timeInputState.hour, timeMinute = timeInputState.minute ) ) } }, dismissButtonText = dismissButtonText, confirmButtonText = confirmButtonText ) } } }
解决方案
1. 手动接管Dialog的焦点控制
通过FocusRequester让Dialog根布局抢占焦点,同时支持点击外部清除TimeInput的焦点,是最直接有效的方法:
修改后的代码如下:
@Suppress("LongParameterList") @Composable fun DateTimeDialog( datePickerState: DatePickerState, timeInputState: TimePickerState, pickerType: CalendarPickerType, title: @Composable () -> Unit = { DefaultTitle() }, headline: @Composable () -> Unit = { DefaultHeadline(state = datePickerState) }, onDismissRequest: () -> Unit, onConfirmRequest: (DateTimeData) -> Unit, dismissButtonText: String = stringResource(id = R.string.cancel), confirmButtonText: String = stringResource(id = R.string.save) ) { val rootFocusRequester = remember { FocusRequester() } Dialog(onDismissRequest = onDismissRequest) { Column( modifier = Modifier .background( Theme.colors.backgroundPrimary, RoundedCornerShape(Theme.spacing.spacing150) ) .focusRequester(rootFocusRequester) .focusable() .pointerInput(Unit) { detectTapGestures(onTap = { // 点击Dialog空白区域时,请求根布局获取焦点 rootFocusRequester.requestFocus() }) }, horizontalAlignment = Alignment.CenterHorizontally ) { DatePicker( modifier = Modifier .clip(RoundedCornerShape(Theme.spacing.spacing150)), title = title, state = datePickerState, headline = headline, dateValidator = { calendarValidator(pickerType, it) }, showModeToggle = false, colors = datePickerColors() ) // 用可聚焦的Box包裹TimeInput,避免其自动抢占焦点 Box(modifier = Modifier.focusable()) { TimeInput( state = timeInputState, colors = timePickerColors() ) } BottomButtons( confirmButtonEnabled = datePickerState.selectedDateMillis != null, onDismissRequest = onDismissRequest, onConfirmRequest = { datePickerState.selectedDateMillis?.let { onConfirmRequest( DateTimeData( dateEpochMilli = it, timeHour = timeInputState.hour, timeMinute = timeInputState.minute ) ) } }, dismissButtonText = dismissButtonText, confirmButtonText = confirmButtonText ) } } // Dialog初始化时,让根布局获取焦点,阻止TimeInput自动聚焦 LaunchedEffect(Unit) { rootFocusRequester.requestFocus() } }
2. 升级Material3库版本
部分旧版本的Material3(比如1.0.x系列)确实存在TimeInput自动聚焦且无法取消的Bug,官方在后续稳定版中已修复。可以尝试将Material3依赖升级到1.1.0及以上版本,验证问题是否消失。
3. 自定义TimeInput输入框焦点属性
如果上述方法无效,可以尝试自定义TimeInput的输入框修饰符,禁用自动聚焦:
不过这种方法需要重写TimeInput的内部组件,复杂度较高,仅作为备选方案:
// 示例:自定义TimeInput的小时输入框修饰符 TimeInput( state = timeInputState, colors = timePickerColors(), // 若TimeInput支持传入输入框修饰符(部分版本支持) hourTextFieldModifier = Modifier.focusProperties { canFocus = false // 禁用自动聚焦,用户点击后仍可手动聚焦 } )
内容的提问来源于stack exchange,提问作者Scoobidy-Bop
相关产品推荐
相关产品推荐

