鸿蒙next 获取验证码输入框 重磅来袭

前言导读

最近能在一个鸿蒙的项目,里面有用到类似安卓和ios验证码输入框,所以就参考网上的一些案例重写了一个鸿蒙版本 那么废话不多说我们正式开始。

开发环境

鸿蒙 Next(基于API12) 实现一个验证码输入组件(VerifyCodeInput)

效果图

具体实现

为了解决输入或删除自动移格通过Text组件来显示验证码而非TextInput,键盘输入通过inputMethod.InputMethodController 自绘控件绑定输入法形式进行输入内容监听,最后通过组件本身 onVisibleAreaChange事件判断页面是隐藏(跳转下一页)或者显示(返回)动态控制键盘弹出和隐藏

基础变量声明
ini 复制代码
verifyCodeInputCallback?:VerifyCodeInputCallback

 @State codeText: string = '';
private inputController: inputMethod.InputMethodController = inputMethod.getController();
private arrArea: number[] = [0.0, 1, 0];
private verifyCodeLength: number = 6;
private codeIndexArray: Array<number> = Array.from([0, 1, 2, 3, 4, 5]);
private textConfig: inputMethod.TextConfig = {
  inputAttribute: {
    textInputType: inputMethod.TextInputType.NUMBER,
    enterKeyType: inputMethod.EnterKeyType.GO
  }
};
private windowClass: window.Window = AppStorage.get('windowClass') as window.Window;
private isFirstOpenPage: boolean = true;
private registerCount: number = 1;

2、自绘控件输入法事件回调设置和事件解绑

kotlin 复制代码
async attach() {
  if (this.isFirstOpenPage) {
    await this.inputController.attach(true, this.textConfig);
    this.listen();
    return;
  }
  try {
    this.windowClass.on('windowEvent', async (windowState) => {
      if (windowState === window.WindowEventType.WINDOW_ACTIVE && this.registerCount === 1) {
        this.registerCount++;
        await this.inputController.attach(true, this.textConfig);
        this.listen();
      }
    });
  } catch (error) {
    hilog.error(0x0000, TAG, `failed to getWindowState callback, error code : ${error.code}`);
  }
}

listen() {
  this.inputController.on('insertText', (text: string) => {
    if (this.codeText.length >= this.verifyCodeLength || isNaN(Number(text)) || text === ' ') {
      return;
    }
    this.codeText += text;
    this.verifyCodeInputCallback?.getinputcode(this.codeText)
  })
  this.inputController.on('deleteLeft', () => {
    this.codeText = this.codeText.substring(0, this.codeText.length - 1);
    this.verifyCodeInputCallback?.getinputcode(this.codeText)
  })
}

detach(): void {
  this.isFirstOpenPage = false;
  this.registerCount = 1;
  this.windowClass.off('windowEvent');
  this.inputController.off('insertText');
  this.inputController.off('deleteLeft');
  this.inputController.detach((_err: BusinessError) => {
  });
}

我们在 aboutToDisappear 进来的时候要调用一下this.detach()默认清空我们的输入框

验证码输入框布局和输入绑定和解绑
typescript 复制代码
@Builder
buildVerifyCodeComponent() {
  Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween }) {
    ForEach(this.codeIndexArray, (item: number) => {
      Text(this.codeText[item])
        .verifyCodeUnitStyle()
        .backgroundColor(this.codeText[item] === undefined ? $r('app.color.index_background') :
        $r('app.color.theme_color_m'))
    }, (item: number) => JSON.stringify(item))
  }
  .onVisibleAreaChange(this.arrArea, async (isVisible: boolean, currentRatio: number) => {
    if (isVisible && currentRatio >= 1.0) {
      this.attach();
    }
    if (!isVisible && currentRatio <= 0.0) {
      this.detach();
    }
  })
  .backgroundColor(Color.Transparent)
  .height($r('app.float.text_height'))
  .margin({
    left: $r('app.float.background_margin'),
    right: $r('app.float.background_margin')
  })
  .defaultFocus(true)
  .onClick(() => {
    this.inputController.attach(true, this.textConfig);
  })
}

定义接口回调输入拿到的结果

我们通过构造方法传入我们的interface

typescript 复制代码
/***
 *
 * 输入框回调
 *
 */
export  interface  VerifyCodeInputCallback{
  getinputcode: (value?:string) => void
}
回调结果当然我们也可以使用@Link装饰器来实现
kotlin 复制代码
listen() {
  this.inputController.on('insertText', (text: string) => {
    if (this.codeText.length >= this.verifyCodeLength || isNaN(Number(text)) || text === ' ') {
      return;
    }
    this.codeText += text;
    //回调我们的结果
    this.verifyCodeInputCallback?.getinputcode(this.codeText)
  })
  this.inputController.on('deleteLeft', () => {
    this.codeText = this.codeText.substring(0, this.codeText.length - 1);
    //    //回调我们的结果
    this.verifyCodeInputCallback?.getinputcode(this.codeText)
  })
}

完整代码

kotlin 复制代码
import { inputMethod } from '@kit.IMEKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { VerifyCodeInputCallback } from './VerifyCodeInputCallback';


/*****
 *
 * 创建人:xuqing
 * 创建时间:2025年4月21日10:51:13
 * 类说明:自定义输入框格子
 *
 */






@Extend(Text)
function verifyCodeUnitStyle() {
  .fontSize($r('app.float.font_size'))
  .fontWeight(FontWeight.Bold)
  .textAlign(TextAlign.Center)
  .width($r('app.float.text_height'))
  .aspectRatio(1)
  .margin({
    left: $r('app.float.text_margin'),
    right: $r('app.float.text_margin')
  })
  .borderRadius($r('app.float.row_width'))
}

const TAG = 'BackgroundColorChange';
@Component
export default struct VerifyCodeInput {

  verifyCodeInputCallback?:VerifyCodeInputCallback

   @State codeText: string = '';
  private inputController: inputMethod.InputMethodController = inputMethod.getController();
  private arrArea: number[] = [0.0, 1, 0];
  private verifyCodeLength: number = 6;
  private codeIndexArray: Array<number> = Array.from([0, 1, 2, 3, 4, 5]);
  private textConfig: inputMethod.TextConfig = {
    inputAttribute: {
      textInputType: inputMethod.TextInputType.NUMBER,
      enterKeyType: inputMethod.EnterKeyType.GO
    }
  };
  private windowClass: window.Window = AppStorage.get('windowClass') as window.Window;
  private isFirstOpenPage: boolean = true;
  private registerCount: number = 1;

  aboutToDisappear(): void {
    this.detach();
  }

  async attach() {
    if (this.isFirstOpenPage) {
      await this.inputController.attach(true, this.textConfig);
      this.listen();
      return;
    }
    try {
      this.windowClass.on('windowEvent', async (windowState) => {
        if (windowState === window.WindowEventType.WINDOW_ACTIVE && this.registerCount === 1) {
          this.registerCount++;
          await this.inputController.attach(true, this.textConfig);
          this.listen();
        }
      });
    } catch (error) {
      hilog.error(0x0000, TAG, `failed to getWindowState callback, error code : ${error.code}`);
    }
  }

  listen() {
    this.inputController.on('insertText', (text: string) => {
      if (this.codeText.length >= this.verifyCodeLength || isNaN(Number(text)) || text === ' ') {
        return;
      }
      this.codeText += text;
      this.verifyCodeInputCallback?.getinputcode(this.codeText)
    })
    this.inputController.on('deleteLeft', () => {
      this.codeText = this.codeText.substring(0, this.codeText.length - 1);
      this.verifyCodeInputCallback?.getinputcode(this.codeText)
    })
  }

  detach(): void {
    this.isFirstOpenPage = false;
    this.registerCount = 1;
    this.windowClass.off('windowEvent');
    this.inputController.off('insertText');
    this.inputController.off('deleteLeft');
    this.inputController.detach((_err: BusinessError) => {
    });
  }

  @Builder
  buildVerifyCodeComponent() {
    Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween }) {
      ForEach(this.codeIndexArray, (item: number) => {
        Text(this.codeText[item])
          .verifyCodeUnitStyle()
          .backgroundColor(this.codeText[item] === undefined ? $r('app.color.index_background') :
          $r('app.color.theme_color_m'))
      }, (item: number) => JSON.stringify(item))
    }
    .onVisibleAreaChange(this.arrArea, async (isVisible: boolean, currentRatio: number) => {
      if (isVisible && currentRatio >= 1.0) {
        this.attach();
      }
      if (!isVisible && currentRatio <= 0.0) {
        this.detach();
      }
    })
    .backgroundColor(Color.Transparent)
    .height($r('app.float.text_height'))
    .margin({
      left: $r('app.float.background_margin'),
      right: $r('app.float.background_margin')
    })
    .defaultFocus(true)
    .onClick(() => {
      this.inputController.attach(true, this.textConfig);
    })
  }



  build() {
    Column() {
      this.buildVerifyCodeComponent();
    }.width('100%')
  }

}

在index 里面调用调试

scss 复制代码
import Logger from './Logger';
import VerifyCodeInput from './VerifyCodeInput';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Column() {
      Row() {
        Text($r('app.string.enter_the_verification_code'))
          .fontSize($r('app.float.font_size_l'))
          .fontWeight(FontWeight.Medium)
      }.height($r('app.float.list_padding_top'))


      Row() {
        Text($r('app.string.Verification_code_sent_to'))
          .fontSize($r('app.float.font_size_m'))
          .fontColor($r('app.color.phone_color'))
          .fontWeight(FontWeight.Regular)
        Text($r('app.string.phone_number1'))
          .fontSize($r('app.float.font_size_m'))
          .fontColor($r('app.color.theme_color'))
          .fontWeight(FontWeight.Regular)
      }
      .margin({
        top: $r('app.float.row_width'),
        bottom: $r('app.float.margin_bottom')
      })
      Row(){
        VerifyCodeInput({verifyCodeInputCallback: {
          getinputcode(data) {
            Logger.error("数据框回调 data   --- > " + data)
          }
        }
        });
      }.width("100%").height(50).margin({top:15})


      Row() {
        Text($r('app.string.reacquire'))
          .fontSize($r('app.float.font_size'))
          .fontWeight(FontWeight.Medium)
          .fontColor($r('app.color.theme_color'))
      }
      .width('90%')
      .margin({ top: $r('app.float.row_margin') })
      .justifyContent(FlexAlign.End)
    }
    .padding({ top: 48})
    .height('100%')
    .width('100%')
  }
}
查看日志打印

我们查看到日志无论是我们删除输入框的内容 还是增加输入框的内容控制台里面都有对应结果的输出

最后总结:

在鸿蒙next 里面确实还有很多功能效果没有现成的组件去实现,但是生态越来越强大,我相信在不久未来 鸿蒙系统的生态会发展得越来越好,有兴趣的同学可以拷贝代码自己去尝试 , 今天的文章就讲到这里有兴趣的 关注我B站教程 了解更多鸿蒙开发的知识 可以关注坚果派公众号 。 谢谢

课程地址

www.bilibili.com/cheese/play...

项目内容:

1 常用布局组件的学习
2 网络请求工具类封装
3 arkui 生命周期启动流程
4 日志工具类的封装
5 自定义组合组件的封装
6 路由导航跳转的使用
7 本地地数据的缓存 以及缓存工具类的封装
8 欢迎页面的实现
9 登录案例和自动登录效果实现
10 请求网络数据分页上拉加载 下拉刷新的实现
11 list数据懒加载实现
12 webview组件的使用

如果使用更多好用的鸿蒙next三方库

团队介绍

团队介绍:坚果派由坚果等人创建,团队由12位华为HDE以及若干热爱鸿蒙的开发者和其他领域的三十余位万粉博主运营。专注于分享 HarmonyOS/OpenHarmony,ArkUI-X,元服务,仓颉,团队成员聚集在北京,上海,南京,深圳,广州,宁夏等地,目前已开发鸿蒙 原生应用,三方库60+,欢迎进行课程,项目等合作。

友情链接

harmony-utils 一款功能丰富且极易上手的HarmonyOS工具库,借助众多实用工具类,致力于助力开发者迅速构建鸿蒙应用,能够满足各种不同的开发需求。

harmony-dialog 一款极为简单易用的零侵入弹窗,仅需一行代码即可轻松实现,无论在何处都能够轻松弹出。

相关推荐
__Benco2 分钟前
OpenHarmony - 小型系统内核(LiteOS-A)(六)
人工智能·harmonyos
别说我什么都不会2 小时前
【仓颉三方库】 数据解析——yaml4cj
harmonyos
HMSCore3 小时前
如何实现应用内支付零掉单?
harmonyos
zhangmeng3 小时前
一文带你读懂鸿蒙Stage模型开发运行期和编译期概念
harmonyos·arkts·arkui
梁下轻语的秋缘7 小时前
HarmonyOs学习 环境配置后 实验1:创建项目Hello World
学习·华为·harmonyos
少年的云河月7 小时前
OpenHarmony Camera开发指导(五):相机预览功能(ArkTS)
harmonyos·openharmony·camera·相机开发
Hello_MyDream7 小时前
鸿蒙语言基础
华为·harmonyos
杯莫停丶7 小时前
对象池模式在uniapp鸿蒙APP中的深度应用
uni-app·harmonyos·鸿蒙
simple_lau9 小时前
浅谈鸿蒙多线程
harmonyos·arkts·arkui