HarmonyOS 鸿蒙Next中BindSheet在获取焦点后的650ms后自动退出
HarmonyOS 鸿蒙Next中BindSheet在获取焦点后的650ms后自动退出
// 添加待办事项的半模态
[@Builder](/user/Builder)
AddTodoSheet() {
Column() {
/*Text('添加待办事项')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 20 })
// 批量添加开关
Row() {
Text('批量添加模式')
.fontSize(16)
.fontColor('#333333')
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: $$this.batchAddMode })
.selectedColor('#007AFF')
.onChange((isOn: boolean) => {
this.batchAddMode = isOn;
})
}
.width('90%')
.margin({ bottom: 15 })
.padding({ left: 5, right: 5 })
// 置顶/常规选择按钮
Row() {
Button('添加置顶事项')
.layoutWeight(1)
.backgroundColor(this.isAddingPinned ? '#007AFF' : '#F0F0F0')
.fontColor(this.isAddingPinned ? Color.White : Color.Black)
.onClick(() => {
this.isAddingPinned = true;
})
Button('添加常规事项')
.layoutWeight(1)
.margin({ left: 10 })
.backgroundColor(!this.isAddingPinned ? '#007AFF' : '#F0F0F0')
.fontColor(!this.isAddingPinned ? Color.White : Color.Black)
.onClick(() => {
this.isAddingPinned = false;
})
}
.width('90%')
.margin({ bottom: 20 })
// 事项内容输入
TextInput({
placeholder: "输入待办事项内容...",
text: this.newTodoText
})
.width('90%')
.height(50)
.margin({ bottom: 15 })
.onChange((value: string) => {
this.newTodoText = value;
})
// 备注输入
TextArea({
placeholder: "添加备注(可选)...",
text: this.newTodoNote
})
.width('90%')
.height(80)
.margin({ bottom: 15 })
.onChange((value: string) => {
this.newTodoNote = value;
})
// 颜色选择
Text('选择颜色')
.width('90%')
.fontSize(16)
.margin({ bottom: 10 })
.textAlign(TextAlign.Start)
this.ColorPicker()
// 操作按钮
Row() {
Button('取消')
.layoutWeight(1)
.backgroundColor('#F0F0F0')
.fontColor(Color.Black)
.onClick(() => {
this.showAddSheet = false;
this.newTodoText = '';
this.newTodoNote = '';
this.selectedColor = TodoColor.RED;
this.batchAddMode = false;
})
Button('添加')
.layoutWeight(1)
.margin({ left: 10 })
.backgroundColor('#007AFF')
.onClick(() => {
this.addTodo();
})
}
.width('90%')
.margin({ bottom: 20 })*/
}
.height('70%')
.backgroundColor(Color.White)
.borderRadius({ topLeft: 20, topRight: 20 })
}
更多关于HarmonyOS 鸿蒙Next中BindSheet在获取焦点后的650ms后自动退出的实战教程也可以访问 https://www.itying.com/category-93-b0.html
开发者你好,这边测试半模态未能复现,为了更快解决您的问题,麻烦请补充以下信息:
针对该问题最好上传一下问题的录屏或者gif图片;
复现代码(如最小复现demo);
版本信息(如:开发工具、手机系统版本信息);
以下是测试代码:
import { promptAction, PromptAction } from '@kit.ArkUI';
@Entry
@Component
struct Page {
@State isShow: boolean = false;
@State enableHoverMode: boolean = true;
@Builder
myBuilder() {
Column() {
}.width(200)
.height('70%')
.backgroundColor(Color.White)
.borderRadius({ topLeft: 20, topRight: 20 })
}
build() {
Column() {
Button("拉起半模态")
.onClick(() => {
this.isShow = true;
})
.fontSize(20)
.margin(10)
.bindSheet(this.isShow, this.myBuilder(), {
height: 300,
backgroundColor: Color.Green,
preferType: SheetType.CENTER,
detents:[SheetSize.LARGE],
onDisappear:()=>{
this.isShow = false;
promptAction.showToast({
message:"关闭弹窗"
})
}
})
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
更多关于HarmonyOS 鸿蒙Next中BindSheet在获取焦点后的650ms后自动退出的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
亲,在吗,再看看完整的代码?可能有助于复现,
试试在Column中添加一个占位按钮维持焦点:
Column() {
Button('PLACEHOLDER').opacity(0)
}
显式设置防误触参数:
.bindSheet($$this.showAddSheet, this.AddTodoSheet(), {
detents: [SheetSize.LARGE],
showClose: false,
enableOutsideInteractive: false
})
移除onDisappear中的状态修改逻辑,仅在用户点击取消/确认时修改showAddSheet值
找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17
对不起,未能解决问题 后面代码已完整列出,大佬可否赏脸一看,查找一下问题?
// 原代码
.bindSheet($$this.showAddSheet, ...)
// 修改后(使用!!双向绑定)
.bindSheet(!!this.showAddSheet, this.AddTodoSheet(), {
detents: [SheetSize.MEDIUM],
preferType: SheetType.CENTER,
duration: 0, // 禁用动画
// 其他配置保持不变...
})
感谢您的回复 但无效,窗口依旧闪退 另注:bindsheet无duration属性,
录屏文件在 https://p.cv0.cn/s/mneIW
仍然没有解决,因为程序没有崩溃,日志未提及错误,而且打开一个半模态不涉及调用任何函数
所以我倾向于这是异步数据加载与Sheet渲染的时序冲突导致的
以下是全部代码,问题解决后会删除
感谢您的回复!
import { promptAction } from '[@kit](/user/kit).ArkUI';
import dataPreferences from '[@ohos](/user/ohos).data.preferences';
// 颜色枚举
enum TodoColor {
RED = 'red',
ORANGE = 'orange',
YELLOW = 'yellow',
GREEN = 'green',
CYAN = 'cyan',
BLUE = 'blue',
PURPLE = 'purple'
}
// 颜色配置
interface ColorConfig {
color: string;
name: string;
}
const COLOR_CONFIG = new Map<TodoColor, ColorConfig>([
[TodoColor.RED, { color: '#FF4444', name: '红色' }],
[TodoColor.ORANGE, { color: '#FF8800', name: '橙色' }],
[TodoColor.YELLOW, { color: '#FFBB33', name: '黄色' }],
[TodoColor.GREEN, { color: '#00C851', name: '绿色' }],
[TodoColor.CYAN, { color: '#00BCD4', name: '青色' }],
[TodoColor.BLUE, { color: '#2196F3', name: '蓝色' }],
[TodoColor.PURPLE, { color: '#9C27B0', name: '紫色' }]
]);
// 增强的待办事项接口
interface EnhancedTodoItem {
id: string;
text: string;
completed: boolean;
createdAt: string;
isPinned: boolean; // 是否置顶
color: TodoColor; // 颜色
note: string; // 备注
}
[@Component](/user/Component)
export struct TodoComponent {
[@State](/user/State) todos: Array<EnhancedTodoItem> = [];
[@State](/user/State) newTodoText: string = '';
[@State](/user/State) newTodoNote: string = '';
[@State](/user/State) selectedColor: TodoColor = TodoColor.RED;
[@State](/user/State) showAddSheet: boolean = false;
[@State](/user/State) showDetailSheet: boolean = false;
[@State](/user/State) selectedTodo: EnhancedTodoItem | null = null;
[@State](/user/State) isAddingPinned: boolean = false;
[@State](/user/State) batchAddMode: boolean = false;
private todoStore: dataPreferences.Preferences | null = null;
async aboutToAppear() {
// 初始化待办事项存储
this.todoStore = await dataPreferences.getPreferences(getContext(this), 'enhanced_todos');
await this.loadTodos();
}
// 加载待办事项
private async loadTodos() {
if (!this.todoStore) return;
try {
const keysObj = await this.todoStore.getAll();
const todoItems: EnhancedTodoItem[] = [];
const entries: [string, string][] = Object.entries(keysObj);
for (const entry of entries) {
const key: string = entry[0];
const value: string = entry[1];
if (key.startsWith('enhanced_todo_')) {
try {
const todoData: Partial<EnhancedTodoItem> = JSON.parse(value);
const todoItem: EnhancedTodoItem = {
id: todoData.id || key,
text: todoData.text || '',
completed: Boolean(todoData.completed),
createdAt: todoData.createdAt || new Date().toISOString(),
isPinned: Boolean(todoData.isPinned),
color: todoData.color || TodoColor.RED,
note: todoData.note || ''
};
todoItems.push(todoItem);
} catch (parseErr) {
console.error('解析待办事项失败: ' + JSON.stringify(parseErr));
}
}
}
// 排序:置顶 > 未完成 > 已完成,然后按创建时间倒序
todoItems.sort((a: EnhancedTodoItem, b: EnhancedTodoItem) => {
// 首先按置顶状态排序(未完成的置顶项优先)
if (!a.completed && !b.completed) {
if (a.isPinned !== b.isPinned) {
return a.isPinned ? -1 : 1;
}
}
// 然后按完成状态排序
if (a.completed !== b.completed) {
return a.completed ? 1 : -1;
}
// 最后按创建时间排序
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
});
this.todos = todoItems;
} catch (err) {
console.error('加载待办事项失败: ' + JSON.stringify(err));
}
}
// 添加待办事项
private async addTodo() {
if (!this.todoStore || !this.newTodoText.trim()) return;
try {
const todoId = 'enhanced_todo_' + new Date().getTime();
const newTodo: EnhancedTodoItem = {
id: todoId,
text: this.newTodoText.trim(),
completed: false,
createdAt: new Date().toISOString(),
isPinned: this.isAddingPinned,
color: this.selectedColor,
note: this.newTodoNote.trim()
};
await this.todoStore.put(todoId, JSON.stringify(newTodo));
await this.todoStore.flush();
// 根据批量添加模式决定是否关闭窗口
if (this.batchAddMode) {
// 批量模式:只重置表单,不关闭窗口
this.newTodoText = '';
this.newTodoNote = '';
this.selectedColor = TodoColor.RED;
} else {
// 普通模式:重置表单并关闭窗口
this.newTodoText = '';
this.newTodoNote = '';
this.selectedColor = TodoColor.RED;
this.showAddSheet = false;
}
await this.loadTodos();
promptAction.showToast({
message: this.isAddingPinned ? '已添加置顶待办事项' : '已添加待办事项',
duration: 2000,
showMode: promptAction.ToastShowMode.DEFAULT,
bottom: 85
});
} catch (err) {
console.error('添加待办事项失败: ' + JSON.stringify(err));
}
}
// 切换待办事项状态
private async toggleTodoStatus(todoId: string, completed: boolean) {
if (!this.todoStore) return;
try {
const todoIndex = this.todos.findIndex(item => item.id === todoId);
if (todoIndex === -1) return;
const originalTodo = this.todos[todoIndex];
// 手动复制所有属性
const updatedTodo: EnhancedTodoItem = {
id: originalTodo.id,
text: originalTodo.text,
completed: !completed,
createdAt: originalTodo.createdAt,
isPinned: originalTodo.isPinned,
color: originalTodo.color,
note: originalTodo.note
};
await this.todoStore.put(todoId, JSON.stringify(updatedTodo));
await this.todoStore.flush();
await this.loadTodos();
promptAction.showToast({
message: updatedTodo.completed ? '已完成待办事项' : '已恢复待办事项',
duration: 2000,
showMode: promptAction.ToastShowMode.DEFAULT,
bottom: 85
});
} catch (err) {
console.error('更新待办事项状态失败: ' + JSON.stringify(err));
}
}
// 切换置顶状态
private async togglePinStatus(todoId: string) {
if (!this.todoStore) return;
try {
const todoIndex = this.todos.findIndex(item => item.id === todoId);
if (todoIndex === -1) return;
const originalTodo = this.todos[todoIndex];
// 手动复制所有属性
const updatedTodo: EnhancedTodoItem = {
id: originalTodo.id,
text: originalTodo.text,
completed: originalTodo.completed,
createdAt: originalTodo.createdAt,
isPinned: !originalTodo.isPinned, // 切换置顶状态
color: originalTodo.color,
note: originalTodo.note
};
await this.todoStore.put(todoId, JSON.stringify(updatedTodo));
await this.todoStore.flush();
await this.loadTodos();
promptAction.showToast({
message: updatedTodo.isPinned ? '已设为置顶' : '已取消置顶',
duration: 2000,
showMode: promptAction.ToastShowMode.DEFAULT,
bottom: 85
});
} catch (err) {
console.error('更新置顶状态失败: ' + JSON.stringify(err));
}
}
// 删除待办事项
private async deleteTodo(todoId: string) {
if (!this.todoStore) return;
try {
await this.todoStore.delete(todoId);
await this.todoStore.flush();
await this.loadTodos();
promptAction.showToast({
message: '已删除待办事项',
duration: 2000,
showMode: promptAction.ToastShowMode.DEFAULT,
bottom: 85
});
} catch (err) {
console.error('删除待办事项失败: ' + JSON.stringify(err));
}
}
// 显示详情
private showTodoDetail(todo: EnhancedTodoItem) {
this.selectedTodo = todo;
this.showDetailSheet = true;
}
// 颜色选择器组件
[@Builder](/user/Builder)
ColorPicker() {
Row() {
ForEach(Object.values(TodoColor), (color: TodoColor) => {
Button() {
Circle()
.width(30)
.height(30)
.fill(COLOR_CONFIG.get(color)?.color ?? '#CCCCCC')
.border({
width: this.selectedColor === color ? 3 : 1,
color: this.selectedColor === color ? '#007AFF' : '#E0E0E0'
})
}
.backgroundColor(Color.Transparent)
.onClick(() => {
this.selectedColor = color;
})
}, (color: TodoColor) => color)
}
.width('90%')
.margin({ bottom: 20 })
.justifyContent(FlexAlign.SpaceAround)
}
// 添加待办事项的半模态
[@Builder](/user/Builder)
AddTodoSheet() {
Column() {
Text('添加待办事项')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 20 })
// 批量添加开关
Row() {
Text('批量添加模式')
.fontSize(16)
.fontColor('#333333')
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.batchAddMode })
.selectedColor('#007AFF')
.onChange((isOn: boolean) => {
this.batchAddMode = isOn;
})
}
.width('90%')
.margin({ bottom: 15 })
.padding({ left: 5, right: 5 })
// 置顶/常规选择按钮
Row() {
Button('添加置顶事项')
.layoutWeight(1)
.backgroundColor(this.isAddingPinned ? '#007AFF' : '#F0F0F0')
.fontColor(this.isAddingPinned ? Color.White : Color.Black)
.onClick(() => {
this.isAddingPinned = true;
})
Button('添加常规事项')
.layoutWeight(1)
.margin({ left: 10 })
.backgroundColor(!this.isAddingPinned ? '#007AFF' : '#F0F0F0')
.fontColor(!this.isAddingPinned ? Color.White : Color.Black)
.onClick(() => {
this.isAddingPinned = false;
})
}
.width('90%')
.margin({ bottom: 20 })
// 事项内容输入
TextInput({
placeholder: "输入待办事项内容...",
text: this.newTodoText
})
.width('90%')
.height(50)
.margin({ bottom: 15 })
.onChange((value: string) => {
this.newTodoText = value;
})
// 备注输入
TextArea({
placeholder: "添加备注(可选)...",
text: this.newTodoNote
})
.width('90%')
.height(80)
.margin({ bottom: 15 })
.onChange((value: string) => {
this.newTodoNote = value;
})
// 颜色选择
Text('选择颜色')
.width('90%')
.fontSize(16)
.margin({ bottom: 10 })
.textAlign(TextAlign.Start)
this.ColorPicker()
// 操作按钮
Row() {
Button('取消')
.layoutWeight(1)
.backgroundColor('#F0F0F0')
.fontColor(Color.Black)
.onClick(() => {
this.showAddSheet = false;
this.newTodoText = '';
this.newTodoNote = '';
this.selectedColor = TodoColor.RED;
this.batchAddMode = false;
})
Button('添加')
.layoutWeight(1)
.margin({ left: 10 })
.backgroundColor('#007AFF')
.onClick(() => {
this.addTodo();
})
}
.width('90%')
.margin({ bottom: 20 })
}
.height('70%')
.backgroundColor(Color.White)
.borderRadius({ topLeft: 20, topRight: 20 })
}
// 待办事项详情的半模态
[@Builder](/user/Builder)
TodoDetailSheet() {
if (this.selectedTodo) {
Column() {
Text('待办事项详情')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 20 })
// 颜色和置顶标识
Row() {
Circle()
.width(20)
.height(20)
.fill(COLOR_CONFIG.get(this.selectedTodo.color)?.color ?? '#CCCCCC')
.margin({ right: 10 })
if (this.selectedTodo.isPinned) {
Text('置顶')
.fontSize(14)
.fontColor('#FF6B35')
.backgroundColor('#FFE5DB')
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.borderRadius(12)
.margin({ right: 10 })
}
if (this.selectedTodo.completed) {
Text('已完成')
.fontSize(14)
.fontColor('#28A745')
.backgroundColor('#D4EDDA')
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.borderRadius(12)
}
}
.width('90%')
.margin({ bottom: 20 })
// 事项内容
Text('事项内容')
.width('90%')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 5 })
.textAlign(TextAlign.Start)
Text(this.selectedTodo.text)
.width('90%')
.fontSize(18)
.padding(15)
.backgroundColor('#F8F9FA')
.borderRadius(10)
.margin({ bottom: 20 })
// 备注(如果有)
if (this.selectedTodo.note) {
Text('备注')
.width('90%')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 5 })
.textAlign(TextAlign.Start)
Text(this.selectedTodo.note)
.width('90%')
.fontSize(16)
.fontColor('#6C757D')
.padding(15)
.backgroundColor('#F8F9FA')
.borderRadius(10)
.margin({ bottom: 20 })
}
// 创建时间
Text('创建时间')
.width('90%')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 5 })
.textAlign(TextAlign.Start)
Text(new Date(this.selectedTodo.createdAt).toLocaleString())
.width('90%')
.fontSize(16)
.fontColor('#6C757D')
.margin({ bottom: 30 })
// 操作按钮
Row() {
Button('关闭')
.layoutWeight(1)
.backgroundColor('#6C757D')
.onClick(() => {
this.showDetailSheet = false;
})
Button(this.selectedTodo.isPinned ? '取消置顶' : '设为置顶')
.layoutWeight(1)
.margin({ left: 10 })
.backgroundColor('#FF6B35')
.onClick(() => {
if (this.selectedTodo) {
this.togglePinStatus(this.selectedTodo.id);
this.showDetailSheet = false;
}
})
Button('删除')
.layoutWeight(1)
.margin({ left: 10 })
.backgroundColor('#DC3545')
.onClick(() => {
if (this.selectedTodo) {
this.deleteTodo(this.selectedTodo.id);
this.showDetailSheet = false;
}
})
}
.width('90%')
.margin({ bottom: 20 })
}
.height('80%')
.backgroundColor(Color.White)
.borderRadius({ topLeft: 20, topRight: 20 })
}
}
build() {
Column() {
// 标题和添加按钮
Row() {
Text('待办事项')
.fontSize(24)
.fontWeight('700')
.fontFamily('HarmonyHeiTi-Bold')
.layoutWeight(1)
Button() {
Text() {
SymbolSpan($r('sys.symbol.plus'))
}
.fontColor('#007AFF')
.fontSize(20)
}
.backgroundColor(Color.Transparent)
.onClick(() => {
this.showAddSheet
在HarmonyOS Next中,BindSheet组件获取焦点后650ms自动退出,可能是由于系统默认行为或组件生命周期控制机制触发。该行为可能与焦点丢失、超时设置或特定交互逻辑相关。建议检查BindSheet的焦点事件处理逻辑及系统自动隐藏策略,确认是否存在默认超时配置。
从日志分析,问题出现在焦点管理机制上。日志显示Sheet在获取焦点后触发了bindsheet lifecycle change to onDisappear state
,这通常是由于焦点丢失导致的自动关闭。
建议检查:
- 确保Sheet内容组件包含可聚焦元素(如TextInput)
- 验证showAddSheet状态管理逻辑,避免在onDisappear中重复设置为false
- 检查是否有其他组件意外获取焦点导致Sheet失焦
可以尝试在Sheet的Column中添加一个默认的TextInput组件,或者检查页面中是否存在自动触发焦点转移的逻辑。