HarmonyOS 鸿蒙Next中光标位置错乱

HarmonyOS 鸿蒙Next中光标位置错乱 TextInput设置手机号格式输入,如何解决修改数据后光标位置错乱的问题

4 回复

开发者你好,可以提供最小可复现的demo看下具体问题,或者参考以下方案进行排查:

【问题定位】 onChange函数的规格为value值变化后执行,所以删除空格、删除数字或者添加数字等编辑操作改变value值,导致数据需要重新格式化,也就是重新赋值,此时光标会位于输入值的末尾。

【分析结论】 在value值变化前就将展示结果和光标位置获取到,之后再进行赋值以及光标位置定位就可以了。

【修改建议】 在value值变化前通过onDidInsert和onDidDelete回调函数计算出预期展示结果以及光标位置进行赋值,

验证发现已实现上述期望效果,但美中不足的是,删除时光标重置位置时会有闪动效果,因此找到了同样的文本输入组件RichEditor,RichEditor是支持图文混排和文本交互式编辑的组件,具有更灵活的编辑输入能力。

使用RichEditor组件达成预期效果,完整示例参考如下:

@Entry
@Component
struct PhoneNumberInputTest {
  private controller: RichEditorController = new RichEditorController()
  private originalPhoneNumber: string = ''
  // 文本字体样式,黑色,加粗
  private phoneNumberStyle: RichEditorTextStyle = {
    fontColor: Color.Black,
    fontWeight: FontWeight.Bold
  }

  insertNumber(value: RichEditorInsertValue) {
    this.controller.deleteSpans({ start: 0 })
    let realOffset = this.getRealOffset(value.insertOffset, true)
    this.originalPhoneNumber = this.originalPhoneNumber.substring(0, realOffset) + value.insertValue +
    this.originalPhoneNumber.substring(realOffset)
    // 最长11位
    this.originalPhoneNumber = this.originalPhoneNumber.substring(0, 11)
    this.controller.addTextSpan(this.getSpacePhoneNumber(), { style: this.phoneNumberStyle })
    let caretOffset = this.getCaretOffset(realOffset, true)
    this.controller.setCaretOffset(caretOffset)

  }

  deleteNumber(value: RichEditorDeleteValue) {
    if (this.controller.getCaretOffset() === 0) {
      // 当前光标位置在首位,直接返回
      return
    }
    this.controller.deleteSpans({ start: 0 })
    let realOffset = this.getRealOffset(value.offset, false)
    // 拼接实际手机号value
    this.originalPhoneNumber =
      this.originalPhoneNumber.substring(0, realOffset) + this.originalPhoneNumber.substring(realOffset + 1)
    // 添加文本内容,如果组件光标闪烁,插入后光标位置更新为新插入文本的后面
    this.controller.addTextSpan(this.getSpacePhoneNumber(), { style: this.phoneNumberStyle })

    let caretOffset = this.getCaretOffset(realOffset, false)
    this.controller.setCaretOffset(caretOffset)
  }

  getRealOffset(offset: number, isInsert: boolean) {
    let realOffset = offset
    if (realOffset >= (isInsert ? 9 : 8)) {
      realOffset -= 2
    } else if (realOffset >= (isInsert ? 4 : 3)) {
      realOffset -= 1
    }
    return realOffset
  }

  // 光标实际位置计算
  getCaretOffset(realOffset: number, isInsert: boolean): number {
    let caretOffset = isInsert ? realOffset + 1 : realOffset
    if (caretOffset >= 7) {
      caretOffset += 2
    } else if (caretOffset >= 3) {
      caretOffset += 1
    }
    return caretOffset
  }

  // 拼接手机号码展示文本
  getSpacePhoneNumber(): string {
    let res = this.originalPhoneNumber
    if (res.length >= 4) {
      res = res.substring(0, 3) + ' ' + res.substring(3)
    }
    if (res.length >= 9) {
      res = res.substring(0, 8) + ' ' + res.substring(8)
    }
    return res
  }

  build() {
    RelativeContainer() {
      RichEditor({ controller: this.controller })
        .align(Alignment.Center)
        .id('PhoneNumberInputTestHelloWorld')
        .width('100%')
        .height(60)
        .backgroundColor(0xFFE3ECE3)
        .borderRadius(30)
        .alignRules({
          center: { anchor: 'container', align: VerticalAlign.Center },
          middle: { anchor: 'container', align: HorizontalAlign.Center }
        })
        .aboutToIMEInput((value: RichEditorInsertValue) => {
          // 输入法输入内容前,触发回调
          if (isNaN(Number(value.insertValue))) {
            return false
          }
          this.insertNumber(value)
          return false
        })
        .aboutToDelete((value: RichEditorDeleteValue) => {
          // 输入法删除内容前,触发回调
          this.deleteNumber(value)
          return false
        })
    }
    .height('100%')
    .width('100%')
  }
}

【总结】 上述场景主要关键点在于重新赋值后光标位置处理以及展示效果,针对常规输入删除场景,使用TextInput就可以满足大部分输入框需求,但也存在一些特殊格式要求的输入场景,会对展示内容进行UI重组,那么推荐使用RichEditor具有更高的灵活性。

【背景知识】

  • TextInput是一种单行文本输入框组件。当输入内容发生变化时,会触发该组件的onChange回调函数,当输入完成时,会触发onDidInsert回调函数,当删除完成时,会触发onDidDelete回调函数。
  • RichEditor是一种支持图文混排和文本交互式编辑的组件。

更多关于HarmonyOS 鸿蒙Next中光标位置错乱的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


可以提供最小复现问题的demo看一下

在HarmonyOS Next中,光标位置错乱通常与输入法兼容性或UI组件渲染异常有关。可尝试切换系统默认输入法,或检查应用是否适配了最新SDK。若问题仅出现在特定应用,需排查该应用的文本输入组件实现。系统级问题可能通过更新至最新鸿蒙版本解决。

在HarmonyOS Next中,TextInput组件处理格式化输入(如手机号)时,光标位置错乱通常是由于文本内容变化与光标位置更新不同步导致的。以下解决方案:

  1. 使用onChange回调监听输入变化,通过selection参数手动控制光标位置:
TextInput({ placeholder: '输入手机号' })
  .onChange((value: string, selection: number) => {
    const formattedValue = formatPhoneNumber(value); // 自定义格式化函数
    this.phoneNumber = formattedValue;
    // 计算新光标位置(根据格式化规则调整)
    const newPosition = calculateNewCursorPosition(value, formattedValue, selection);
    this.cursorPosition = newPosition;
  })
  .selection(this.cursorPosition)
  1. 格式化时保留非数字字符(如空格),避免因字符增删导致位置计算偏差:
const formatPhoneNumber = (value: string) => {
  const cleaned = value.replace(/\D/g, '');
  return cleaned.replace(/(\d{3})(\d{4})(\d{4})/, '$1 $2 $3'); // 188 8888 8888
};
  1. 对于复杂格式化场景,可结合TextInputController精确控制选中区域:
const controller = new TextInputController();

// 在格式化后重置选中区域
controller.setSelection({
  start: newPosition,
  end: newPosition
});

关键点:在格式化文本时需同步计算光标偏移量,特别是当插入/删除分隔符时。建议通过正则匹配实现位置映射,确保光标始终停留在预期数字位置。

回到顶部