HarmonyOS 鸿蒙Next 多TextInput输入框焦点自动跳转问题

HarmonyOS 鸿蒙Next 多TextInput输入框焦点自动跳转问题 需求背景:需要做一个多输入框的验证码模块,输入验证码时输入后光标会自动跳转至下一个输入框,删除验证码时会自动删除上一个输入框内容,并且光标跳转至上一个输入框内。6位验证码全部输完后进行登录请求。

具体实现效果如下图:

思路分析:

1.组件化

这个验证码输入框每个都样式和交互逻辑都非常相似,将其提取出作为组件化编写,减少代码冗余,先将单个输入框的样式进行编写

@Component
struct codeInputView {
  code: string
  keyNo: number
  keyStr: string

  build() {
    Row() {
      Column() {
        TextInput({text: this.code})
          .backgroundColor($r('app.color.smart_F5F5F5'))
          .maxLength(1)
          .type(InputType.Number)
          .align(Alignment.Center)
          .width((screenWidthPxToVp - 87)/6 - 10)
      }
      .width((screenWidthPxToVp - 87)/6)
    }
    .height(55)
    .backgroundColor($r('app.color.smart_F5F5F5'))
    .borderRadius(8)
  }
}

2.实现输入的时候,输入后光标跳转至下一个输入框

通过查阅开发文档和相关资料,发现可以将focusControl和key配合使用进行切换聚焦组件,切换焦点

TextInput焦点问题 我想实现类似验证码4个框那种 输入完点完成自动跳到第二个焦点

@Component
struct codeInputView {
  @State code:string
  keyNo: number
  keyStr: string
  // 将code值传递到外部
  transfer: (number) => void
  // 登录请求回调
  requestAction?: () => void

  build() {
    Row() {
      Column() {
        TextInput({text: this.code})
          .backgroundColor($r('app.color.smart_F5F5F5'))
          .maxLength(1)
          .type(InputType.Number)
          .align(Alignment.Center)
          .width((screenWidthPxToVp - 87)/6 - 10)
          .onChange((value) => {
            if (value.length == 1) {
              // 当输入框中有验证码输入
              // 当不是最后一个输入框时,焦点跳转到下一个输入框
              if (this.keyNo != 5) {
                // 验证码赋值
                this.code = value
                // 验证码传递至外部
                this.transfer(value)
                // 切换焦点到下一个输入框
                let nextKeyNo: number = this.keyNo + 1
                let nextKeyStr = 'code' + nextKeyNo
                Log.info('nextKeyStr = ' + nextKeyStr)
                focusControl.requestFocus(nextKeyStr)
                
              } else {
              // 当最后一个输入框时,传值并且进行登录请求回调
                this.transfer(value)
                this.requestAction()
              }
            }
          })
          .onFocus(() => {
             // 获取焦点时进行日志打印查看
             Log.info('get focus = ' + this.keyStr)
          })
          // 设置输入框key值
          .key(this.keyStr)
      }
      .width((screenWidthPxToVp - 87)/6)
    }
    .height(55)
    .backgroundColor($r('app.color.smart_F5F5F5'))
    .borderRadius(8)
  }
}

// 页面代码
@Entry
@Component
export default struct VerifyLoginPage {
  private inputVerify: string = '请输入验证码'
  private hadSentToPhone: string = '已发送验证码到您的手机号'
  private code0: string
  private code1: string
  private code2: string
  private code3: string
  private code4: string
  private code5: string

  @State phone: string = ''

  onPageShow() {
    this.phone = router.getParams()?.['phone']
  }

  build() {
    Flex({direction: FlexDirection.Column, alignItems: ItemAlign.Start}) {
      Image($r('app.media.back_icon'))
        .size({width: 33, height: 33})
        .onClick(() => {
          Log.info('click back button')
          router.back()
        })

      Text(this.inputVerify)
        .textStyle(25, $r('app.color.smart_24292B'), FontWeight.Medium)
        .margin({top: 32})

      Text(this.hadSentToPhone + this.phone)
        .textStyle(14, $r('app.color.smart_9DA2A5'), FontWeight.Regular)
        .margin({top: 12})

      // 验证码输入框
      Flex({direction: FlexDirection.Row, alignItems: ItemAlign.Start, justifyContent: FlexAlign.SpaceBetween}) {
        codeInputView({keyNo: 0, keyStr: 'code0', transfer: (num) => {this.code0 = num}})

        codeInputView({keyNo: 1, keyStr: 'code1', transfer: (num) => {this.code1 = num}})
          .margin({left: 11})

        codeInputView({keyNo: 2, keyStr: 'code2', transfer: (num) => {this.code2 = num}})
          .margin({left: 11})

        codeInputView({keyNo: 3, keyStr: 'code3', transfer: (num) => {this.code3 = num}})
          .margin({left: 11})

        codeInputView({keyNo: 4, keyStr: 'code4', transfer: (num) => {this.code4 = num}})
          .margin({left: 11})

        codeInputView({keyNo: 5, keyStr: 'code5', transfer: (num) => {this.code5 = num}, requestAction: () => {this.loginRequest()}})
          .margin({left: 11})
      }
      .margin({top: 80})
    }
    .width('100%')
    .padding({left: 16, right: 16})
  }
  
// 登录请求
loginRequest() {
  let code = this.code0 + this.code1 + this.code2 + this.code3 + this.code4 + this.code5
  Log.info('phone = ' + this.phone)
  Log.info('code = ' + code)
  // 登录网络请求
  // xxxxxxx
}

此时,可以实现当输入后,光标可以跳转至下一个输入框中,并且可以将输入的code传递至组件外部进行存储在页面中

3.实现删除时,删除前一个输入框内容,并且光标跳转至前一个输入框中

通过日志打印发现,输入删除按钮,onChange方法无法获取到删除的value值。于是通过查看开发文档,发现可以通过onKeyEvent(event: KeyEvent) => {}方法,获取到删除指令,并且通过打印日志发现,每次点击删除按键,都会执行两次删除方法。

这在一开始非常奇怪和难受,因为我想用同样key的值递减来控制光标的移动,而每次删除时,都会往前跳两个输入框,并且输入框的内容不会删除,所以由此想到可以一次删除方法来进行光标跳转,一次删除方法用来删除输入框的内容。

以下为更新后最终代码

@Component
struct codeInputView {
  @State code: string = ''
  keyNo: number
  keyStr: string
  transfer: (number) => void
  requestAction?: () => void

  build() {
    Row() {
      Column() {
        TextInput({text: this.code})
          .backgroundColor($r('app.color.smart_F5F5F5'))
          .maxLength(1)
          .type(InputType.Number)
          .align(Alignment.Center)
          .width((screenWidthPxToVp - 87)/6 - 10)
          .onChange((value) => {
            if (value.length == 1) {
              if (this.keyNo != 5) {
                let nextKeyNo: number = this.keyNo + 1
                let nextKeyStr = 'code' + nextKeyNo
                Log.info('nextKeyStr = ' + nextKeyStr)
                this.code = value
                this.transfer(value)
                focusControl.requestFocus(nextKeyStr)
              } else {
                this.transfer(value)
                this.requestAction()
              }
            }
          })
          .onFocus(() => {
             Log.info('get focus = ' + this.keyStr)
             Log.info('thisCode = ' + this.code)
          })
          // 删除验证码时执行的方法回调
          .onKeyEvent((event: KeyEvent) => {
            if (event.keyCode == KeyCode.KEYCODE_DEL) {
              // 当code值不为空时,删除code值
              if (this.code != '') {
                this.code = ''

              } else if (this.keyNo > 0) {
              // 如果不是第一个输入框,则每次递减keyNo使光标进行跳转至前一个输入框内
                let preKeyNo: number = this.keyNo - 1
                let preKeyStr = 'code' + preKeyNo
                Log.info('preKeyStr = ' + preKeyStr)
                focusControl.requestFocus(preKeyStr)
              }
            }
          })
          .key(this.keyStr)
      }
      .width((screenWidthPxToVp - 87)/6)
    }
    .height(55)
    .backgroundColor($r('app.color.smart_F5F5F5'))
    .borderRadius(8)
  }
}

// 页面代码
@Entry
@Component
export default struct VerifyLoginPage {
  private inputVerify: string = '请输入验证码'
  private hadSentToPhone: string = '已发送验证码到您的手机号'
  private code0: string
  private code1: string
  private code2: string
  private code3: string
  private code4: string
  private code5: string

  @State phone: string = ''

  onPageShow() {
    this.phone = router.getParams()?.['phone']
  }

  build() {
    Flex({direction: FlexDirection.Column, alignItems: ItemAlign.Start}) {
      Image($r('app.media.back_icon'))
        .size({width: 33, height: 33})
        .onClick(() => {
          Log.info('click back button')
          router.back()
        })

      Text(this.inputVerify)
        .textStyle(25, $r('app.color.smart_24292B'), FontWeight.Medium)
        .margin({top: 32})

      Text(this.hadSentToPhone + this.phone)
        .textStyle(14, $r('app.color.smart_9DA2A5'), FontWeight.Regular)
        .margin({top: 12})

      // 验证码输入框
      Flex({direction: FlexDirection.Row, alignItems: ItemAlign.Start, justifyContent: FlexAlign.SpaceBetween}) {
        codeInputView({keyNo: 0, keyStr: 'code0', transfer: (num) => {this.code0 = num}})

        codeInputView({keyNo: 1, keyStr: 'code1', transfer: (num) => {this.code1 = num}})
          .margin({left: 11})

        codeInputView({keyNo: 2, keyStr: 'code2', transfer: (num) => {this.code2 = num}})
          .margin({left: 11})

        codeInputView({keyNo: 3, keyStr: 'code3', transfer: (num) => {this.code3 = num}})
          .margin({left: 11})

        codeInputView({keyNo: 4, keyStr: 'code4', transfer: (num) => {this.code4 = num}})
          .margin({left: 11})

        codeInputView({keyNo: 5, keyStr: 'code5', transfer: (num) => {this.code5 = num}, requestAction: () => {this.loginRequest()}})
          .margin({left: 11})
      }
      .margin({top: 80})
    }
    .width('100%')
    .padding({left: 16, right: 16})
  }
  
// 登录请求
loginRequest() {
  let code = this.code0 + this.code1 + this.code2 + this.code3 + this.code4 + this.code5
  Log.info('phone = ' + this.phone)
  Log.info('code = ' + code)
  // 登录网络请求
  // xxxxxxx
}

此次在codeInputView组件的初始化中code值有进行修改,添加了’'空字符串,这是因为通过打印日志发现,如果不进行赋值的初始化时,code值会为undefined,这个在删除code值时会非常奇怪,所以进行了设空字符串进行初始化

最终即可实现需求所要求的交互逻辑

当前HarmonyOs仍在初步学习过程中,此贴子主要用于自己技术总结,以便后续使用查看,也分享给同样遇到相同问题的开发者们,当前代码若有不太合理之处也请各位大佬多多指点!可以一起沟通交流,感谢各位大佬!


更多关于HarmonyOS 鸿蒙Next 多TextInput输入框焦点自动跳转问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

15 回复

软键盘onKeyEvent事件无法触发怎么办

更多关于HarmonyOS 鸿蒙Next 多TextInput输入框焦点自动跳转问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


有人用过楼主的思想在onSubmit里写组件间的光标跳转吗?onSubmit回车之后【即使.enterKeyType(EnterKeyType.Next)】默认会把键盘关闭,我再去focusControl.requestFocus(nextKeyStr)就会呈现一个键盘先关闭再弹起的效果,这样看起来很不流畅,按理想状态来说,设置了EnterKeyType.Next点击‘下一步’应该是流畅的光标跳转到下一个输入框吧?但是onSubmit默认会关闭键盘。有什么办法解决流畅问题的?

.onSubmit(() => {
  let nextKeyNo: number = this.keyNo + 1;
  let nextKeyStr: string = 'code' + nextKeyNo;
  focusControl.requestFocus(nextKeyStr);
})

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:BV1S4411E7LY/?p=17

你这个是模拟器的操作吧,onKeyEvent只支持遥控器、鼠标、键盘类的,目前不支持真机输入法上面的~

真机上怎么获取呢,


相关信息如下:

  • 字体大小调整
  • 深色模式切换

<div>
    <p>真机上怎么获取呢,</p>
    <h3>相关信息如下:</h3>
    <ul>
        <li>字体大小调整</li>
        <li>深色模式切换</li>
    </ul>
</div>

希望HarmonyOS能继续优化系统稳定性,减少崩溃和重启的情况。

项目名称

  • 状态:已解决
  • 语言:Java
  • 标签:bug,修复

描述

这是一段描述文本。

解决方案

这是解决方案内容。

兄弟

screenWidthPxToVp怎么获取的?

这是自定义以vp为单位的屏幕宽度常量,

HarmonyOS的分布式文件系统让我在多设备间共享文件变得更加方便。

onKeyEvent不走进去,兄弟有遇到吗

没有遇到诶,我的代码就和上面一样的,

我直接反过来用一个TextInput盖着,用Text显示。不用处理焦点和删除问题,

项目名称

描述: 这是一个示例项目

状态: 完成

技术栈

  • HTML
  • CSS
  • JavaScript

成员

  • 张三
  • 李四
  • 王五

基本信息

这里是一些基本信息,但是根据要求不会显示这部分内容。

针对HarmonyOS鸿蒙Next系统中多TextInput输入框焦点自动跳转问题,这通常与输入框的焦点管理或事件处理有关。以下是一些可能的原因及解决方案的简要概述:

  1. 焦点顺序问题:检查TextInput输入框的焦点顺序设置,确保它们按照预期的顺序接收焦点。在鸿蒙系统中,可以通过设置输入框的属性来控制焦点顺序。

  2. 事件冲突:检查是否有其他UI元素或逻辑代码干扰了输入框的焦点管理。例如,按钮点击事件或其他触摸事件可能不小心触发了焦点跳转。

  3. 输入法问题:有时候,第三方输入法或系统自带的输入法可能与鸿蒙系统的焦点管理机制不兼容,导致焦点异常跳转。尝试更换输入法或更新系统输入法版本。

  4. 代码逻辑错误:检查与输入框焦点相关的代码逻辑,确保没有错误地触发焦点跳转。特别是在处理多个输入框时,要确保焦点跳转的逻辑正确无误。

如果上述方法仍然无法解决问题,可能是系统级的bug或特定场景下的异常行为。此时,建议直接联系鸿蒙系统的官方客服或开发者支持团队获取更专业的帮助。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部