HarmonyOS鸿蒙Next中点击MenuItem唤起模态窗口bindsheet时首次闪现

HarmonyOS鸿蒙Next中点击MenuItem唤起模态窗口bindsheet时首次闪现 状况如下图所示:

图片

状况说明:

上图中进行了两次唤起模态窗口的操作。

操作一:

点击菜单项“添加”后模态窗口闪现一次后便消失,再点击右上角的“四点”按钮才会正常出现。正常的逻辑应该是点击菜单项”添加“时唤起窗口,而不是点击右上角”四点“按钮唤起模态窗口。

操作二:

点击菜单项“设置”后模态窗口闪现一次后便消失,再点击右上角的“四点”按钮才会正常出现。正常的逻辑应该是点击菜单项”设置“时唤起窗口,而不是点击右上角”四点“按钮唤起模态窗口。

可能有用的信息:

EcoStudio的一条报错信息:UseImplicitAnimation: Read ret failed

感谢!


更多关于HarmonyOS鸿蒙Next中点击MenuItem唤起模态窗口bindsheet时首次闪现的实战教程也可以访问 https://www.itying.com/category-93-b0.html

15 回复

这个是因为你的 bindSheet 放错位置了,当你点击后这个按钮是会关闭的

cke_947.png

那对应的那这个半模态也会销毁,将其放其他地方就好了

比如这里: cke_2961.png

对应代码我提交到你的仓库了

更多关于HarmonyOS鸿蒙Next中点击MenuItem唤起模态窗口bindsheet时首次闪现的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


感谢小伙伴的解答!目前一共有两处场景需要“模态窗口”:一处如你所写的绑到Row容器上,另外一处绑到Row容器的子组件Image上。如果有多个场景需要“模态窗口”,那么可能出现组件绑定焦虑,大家一起探索更好的方案。

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

小伙伴你好我实现了最小demo复现没有问题,目前推测极大概率是状态变量更新或者是绑定的问题,建议仔细检查一下

图片

@Component
export  struct MenuItemCase {
  @State bindSheetIsShow: boolean = false;

  @Builder
  SubMenu() {
    Menu() {
      MenuItem({ content: "复制", labelInfo: "Ctrl+C" })
      MenuItem({ content: "粘贴", labelInfo: "Ctrl+V" })
    }
  }

  @Builder
  MyMenu() {
    Menu() {
      MenuItem({
        startIcon: $r('app.media.startIcon'),
        content: "添加",
      })
        .onClick(() => {
          this.bindSheetIsShow = !this.bindSheetIsShow
        })
      MenuItem({
        startIcon: $r('app.media.startIcon'),
        content: "菜单选项",
        builder: (): void => this.SubMenu()
      })
    }
  }

  @Builder
  myBuilder() {
    Column() {
      Text('我是bindSheetIsShow')
        .fontSize(20)
    }
    .width('100%')
    .height(300)
  }

  build() {
    Row() {
      Button('点我出现bindMenu')
      .bindMenu(this.MyMenu)
      .width('100%')
    }
    .height('100%')
    .bindSheet($$this.bindSheetIsShow, this.myBuilder())
  }
}

他这个是bindSheet放在Menu里面,关闭Menu的时候一起把bindSheet关了,

好吧,但是为啥不挂在外部容器上。,

我原本的想法是在点击菜单项MenuItem时,唤起模态窗口。

你这个应该是UI刷新问题,点击MenuItem后关闭了Menu,导致bindsheet也不见了,但是状态还保持,所以第二次直接打开了,你用这个代码看看?(按钮是测试的,在你代码原来基础上写这个按钮也不会出现问题,就是UI问题吧)

import { SymbolGlyphModifier } from "@kit.ArkUI"

@Entry
@ComponentV2
export struct Index {
  @Local isShowBindSheet: boolean = false
  @Local isShowBindSheetOfSettingPage: boolean = false

  @Builder
  myMenu() {
    Menu() {
      MenuItem({
        symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.plus')),
        content: "添加"
      })
        .onClick(() => {
          this.isShowBindSheet = true
        })
      MenuItem({
        content: '编辑',
      })
      MenuItem({
        content: '删除'
      })
        .onClick(() => {
          const uiContext: UIContext = this.getUIContext();
          uiContext.showAlertDialog({
            title: '删除',
            subtitle: '删除后不可恢复',
            message: `确认删除吗?`,
            autoCancel: true,
            buttonDirection: DialogButtonDirection.HORIZONTAL,
            buttons: [
              {
                value: '确定',
                action: () => {
                  uiContext.getPromptAction().showToast({
                    message: `已被删除`,
                    duration: 2000
                  })
                }
              },
              {
                value: '取消',
                action: () => {
                  uiContext.getPromptAction().showToast({
                    message: '已取消',
                    duration: 2000
                  })
                }
              }
            ]
          })
        })
      MenuItem({
        content: "设置"
      })
        .onClick(() => {
          this.isShowBindSheetOfSettingPage = true
        })
    }
  }

  @Builder
  bindsheetOfAddResources() {
    Column() {
      TextInput({ placeholder: '请输入...', text: '' })
        .margin({ bottom: 30 })
        .enterKeyType(EnterKeyType.Done)
        .onChange((value) => {

        })
      Button('添加')
        .onClick(() => {
          this.isShowBindSheet = false
        })
    }
    .margin({ top: 60 })
  }

  @Builder
  bindsheetOfSettings() {
    Column() {
      Text('设置页面')
    }
  }

  build() {
    Column() {
      Row() {
        Row({ space: 10 }) {
          SymbolGlyph($r('sys.symbol.eye_slash'))
            .height(30)
            .onClick(() => {
            })

          SymbolGlyph($r('sys.symbol.dot_grid_2x2'))
            .height(30)
            .bindMenu(this.myMenu())

          // Button('测试按钮')
          //   .onClick(() => {
          //     this.isShowBindSheet = true
          //   })
          //   .bindSheet(this.isShowBindSheet, this.bindsheetOfAddResources(), {
          //     title: {
          //       title: $r('app.string.titleOfBindsheetOfAddResources')
          //     },
          //     onWillDismiss: () => {
          //       this.isShowBindSheet = false
          //     }
          //   })

          Column()
            .bindSheet(this.isShowBindSheet, this.bindsheetOfAddResources(), {
              title: {
                title: $r('app.string.titleOfBindsheetOfAddResources')
              },
              onWillDismiss: () => {
                this.isShowBindSheet = false
              }
            })

          Column()
            .bindSheet(this.isShowBindSheetOfSettingPage, this.bindsheetOfSettings(), {
              title: { title: $r('app.string.titleOfBindsheetOfSettings') },
              onWillDismiss: () => {
                this.isShowBindSheetOfSettingPage = false
              }
            })
        }
      }
      .margin({ top: 30, right: 10 })
      .alignSelf(ItemAlign.End)
    }
  }
}

项目仓库👈项目地址,可拉到本地复现问题。

代码片段:

@Entry
@ComponentV2
export struct Index {

@Builder
  myMenu() {
    Menu() {
      MenuItem({
        symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.plus')),
        content: "添加"
      })
     
        .onClick(() => {
          this.isShowBindSheet = true
        })
        
        .bindSheet(this.isShowBindSheet, this.bindsheetOfAddResources(), {
          title: { title: $r('app.string.titleOfBindsheetOfAddResources') },
          onWillDismiss: () => {
            this.isShowBindSheet = false
          }
        })
      MenuItem({
        content: '编辑',
        builder: () => this.SubMenu1()
      })
      MenuItem({
        content: '删除'
      })
        .onClick(() => {
          const uiContext: UIContext = this.getUIContext();
          uiContext.showAlertDialog({
            title: '删除',
            subtitle: '删除后不可恢复',
            message: `确认删除吗?`,
            autoCancel: true,
            buttonDirection: DialogButtonDirection.HORIZONTAL,
            buttons: [
              {
                value: '确定',
                action: () => {
                  uiContext.getPromptAction().showToast({
                    message: `${this.readingFeed.readingFeed}已被删除`,
                    duration: 2000
                  })
                }
              },
              {
                value: '取消',
                action: () => {
                  uiContext.getPromptAction().showToast({
                    message: '已取消',
                    duration: 2000
                  })
                }
              }
            ]
          })
        })
      MenuItem({
        content: "设置"
      })
        .onClick(() => {
          this.isShowBindSheetOfSettingPage = true
        })       
        .bindSheet(this.isShowBindSheetOfSettingPage, this.bindsheetOfSettings(), {
          title: { title: $r('app.string.titleOfBindsheetOfSettings') },
          onWillDismiss: () => {
            this.isShowBindSheetOfSettingPage = false
          }
        })
    }

    
  }

  @Builder
  bindsheetOfAddResources() {
    Column() {     
      TextInput({ placeholder: '请输入...', text: '' })
        .margin({ bottom: 30 })
        .enterKeyType(EnterKeyType.Done)
        .onChange((value) => {
          this.inputTempValuel = value
        })
      Button('添加')
        .onClick(() => {        
          })
          this.isShowBindSheet = false
        })
    }
    .margin({ top: 60 })
  }

  @Builder
  bindsheetOfSettings() {
    Column() {
      Text('设置页面')
    }
  }

  build(){
    Column(){
      Row() {
        Row({ space: 10 }) {
          Image(this.isReaded ? $r('app.media.eye_slash') : $r('app.media.eye'))
            .height(30)
            .onClick(() => {
              if (this.isReaded == true) {
                this.isReaded = false
              } else {
                this.isReaded = true
              }
            })

          Image($r('app.media.dot_grid_2x2'))
            .height(30)
            .bindMenu(this.myMenu())
        }
      }
      .margin({ top: 30, right: 10 })
      .alignSelf(ItemAlign.End)
    }
  }

}

代码放一下?

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

使用Docker部署MySQL

1. 拉取MySQL镜像

docker pull mysql:latest

2. 创建数据卷

docker volume create mysql_data
docker volume create mysql_config

3. 运行MySQL容器

docker run -d \
  --name mysql-server \
  -e MYSQL_ROOT_PASSWORD=your_password \
  -v mysql_data:/var/lib/mysql \
  -v mysql_config:/etc/mysql/conf.d \
  -p 3306:3306 \
  mysql:latest

4. 进入MySQL容器

docker exec -it mysql-server mysql -uroot -p

5. 常用命令

  • 查看运行中的容器
docker ps
  • 停止MySQL容器
docker stop mysql-server
  • 启动MySQL容器
docker start mysql-server
  • 删除MySQL容器
docker rm mysql-server

6. 配置文件

MySQL配置文件位于 /etc/mysql/conf.d 目录下,可以通过修改配置文件来调整MySQL参数。

在HarmonyOS Next中,MenuItem点击首次唤起bindsheet模态窗口时出现闪现,通常是由于窗口初始化渲染时序问题导致。可能涉及UI组件加载与显示动画的瞬时冲突。建议检查Sheet组件的状态绑定与显示控制逻辑,确保在完全加载后再执行过渡动画。可尝试使用延迟渲染或预加载机制优化。

这是一个典型的模态窗口(bindSheet)在首次点击时因动画或状态管理问题导致的闪现Bug。从你提供的GIF和报错信息来看,核心原因可能是组件的初始状态与动画触发时机冲突

问题分析:

  1. 根本原因bindSheet 控制的模态窗口,其显示状态(通过 isShow 绑定)可能在首次设置时,与组件的初始渲染或隐式动画(UseImplicitAnimation)产生了竞争条件。报错 UseImplicitAnimation: Read ret failed 直接指向了隐式动画读取失败,这通常意味着在动画开始或计算时,依赖的状态或属性尚未准备好或发生了突变。
  2. 现象解释
    • 首次点击闪现:点击MenuItem时,isShow 被设置为 true,触发了窗口的显示动画。但由于上述状态/动画冲突,窗口在开始执行“进入动画”的瞬间就立即中断或重置了状态(可能被意外设为 false),导致你只看到一闪而过。
    • 二次点击正常:通过右上角“四点”按钮再次触发时,组件和动画状态已经过首次错误的“初始化”,竞争条件消失,因此能正常显示。

解决方案:

请检查并修正你的 bindSheet 调用代码,确保状态变更在一个稳定的上下文中。以下是一个关键点示例:

// 可能存在问题的模式:状态可能在渲染周期中被意外干扰
@State isShowSheet: boolean = false;
...
bindSheet(this, {
  isShow: this.isShowSheet,
  ...
})
...
// 点击MenuItem的事件处理函数
onMenuItemClick() {
  this.isShowSheet = true; // 直接切换可能触发问题
}

建议修改方向:

  1. 使用延迟触发:在首次显示时,使用 setTimeoutnextFrame 将状态变更包裹,确保其脱离当前的渲染/动画周期。
    onMenuItemClick() {
      // 使用微任务或下一帧延迟设置状态
      setTimeout(() => {
        this.isShowSheet = true;
      }, 0);
      // 或使用 nextFrame (如果环境支持)
    }
    
  2. 检查状态依赖:确认 isShow 绑定的状态(如 isShowSheet)没有在其他地方(如生命周期函数、其他事件回调)被意外修改,尤其是在首次渲染期间。
  3. 简化动画:如果模态窗口的动画配置复杂,尝试暂时移除自定义动画,使用默认动画,以排除动画配置导致的冲突。
  4. 组件结构:确保 bindSheet 与触发它的MenuItem在正确的组件层级中,状态提升或传递路径清晰,避免不必要的重新渲染。

优先尝试第1点(延迟触发状态变更),这能有效规避多数首次渲染时的动画竞争问题。如果问题依旧,再结合第2、3点检查代码逻辑。

回到顶部