HarmonyOS一杯冰美式的时间 -- 验证码框

一、前言

像是短密码、验证码都有可能需要一个输入框,像是如下:

恰好在写HarmonyOS的时候也需要写一个验证码输入框,但是在实现的时候碰了几次灰,觉得有必要分享下,故有了此篇文章。

如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏 PS:二三为错误示例,如果你只想要代码,在四开始

二、ForEach + TextInput

一开始直接上手就是使用Android的老方案,使用多个EditText,只需要切换焦点即可。在HarmonyOS中对应的就是TextInput。因为需要数个相同的输入框,我们先写一个通用的输入框。

 @Component
 struct CodeInputView {
   build() {
     TextInput()
       .backgroundColor("#CCFFFFFF")
       .borderRadius(10)
       .maxLength(1)
       .type(InputType.Number)
       .align(Alignment.Center)
   }
 }

如果一个个去添加输入框,太麻烦了,如果有改动也很头大,所以我们可以塞到一个父布局中,使用ForEach来添加。因为这种情形的输入一般是横向的,使用Row是一个很好的主意,所以变成了“在Row中使用ForEach添加若干个TextInput”,我们稍微修改下:

 @Preview
 @Component
 struct CodeInputView {
   // 创建一个包含5个空字符串的数组,用于存储输入的数字
   @State codeKids: Array<string> = new Array(5).fill('')
 ​
   // 构建界面
   build() {
     Row({ space: 10 }) {
       ForEach(this.codeKids, (item: string, index: number) => {
         TextInput(this.codeKids[index])
           .backgroundColor("#CCFFFFFF") // 设置文本输入框的背景颜色
           .borderRadius(10) // 设置文本输入框的圆角
           .maxLength(1) // 设置最大输入长度为1
           .layoutWeight(1) // 设置布局权重
           .fontSize(25) // 设置字体大小
           .height("100%") // 设置高度为100%
           .type(InputType.Number) // 设置输入类型为数字
           .align(Alignment.Center) // 设置文本居中对齐
       }, (item: string) => item)
     }.backgroundColor(Color.Black) // 设置整个行的背景颜色为黑色,方便preview
     .height(80) // 设置行的高度为80
   }
 }

如果我们逐个手动添加输入框,会显得非常繁琐,而且如果需要进行修改的话也会变得很复杂。

因此,我们可以将这些输入框放置在一个父布局中,然后使用 ForEach 函数来动态添加它们。由于这种情况下输入框通常是水平排列的,所以使用 Row 组件是一个明智的选择。因此,我们将代码改成了 ‘在 Row 中使用 ForEach 动态添加多个 TextInput’ 的方式。

我们新增了一个名为 codeKids 的数组,并用空字符进行了填充,并使用 @State 注解来修饰它。在 RowForEach 中,我们直接使用 codeKids 作为数据源,这样输入框的数量会根据 codeKids 数组的长度而变化,而 codeKids 的大小就代表了验证码的长度。

layoutWeight(1){ space: 10 } 这两个组合参数,实现了等宽和等间距的效果。

通过@Preview,我们已经能看到效果了。

接下来我们需要它动起来,也就是"输入一个切换到下一个输入框,最后一个返回完整的验证码"。

这里显然需要我们使用onChange方法监听字符的输入。

分解一下

  1. 监听每个 TextInputonChange 事件,当用户输入字符后,将字符存入相应位置的 codeKids 数组,并移动焦点到下一个 TextInput
  2. 在最后一个输入框中,当用户输入字符后,将字符存入 codeKids 数组,并触发验证码完成的操作。

需要注意的是,并不能使用focusable(true)来达到将焦点赋予给某个输入框的操作,移动焦点需要使用focusControl.requestFocus(),而requestFocus需要的参数是输入框的key,这里我们需要新增一个key:

 @Preview
 @Component
 struct CodeInputView {
   // 用于存储用户输入的字符的数组,初始值为5个空字符串
   @State codeKids: Array<string> = new Array(5).fill('')
   
   // 回调函数,用于传递输入结果给父组件
   inputResultCallback: (string) => void
 ​
   build() {
     // 创建一个横向排列的行,每个输入框之间有一定的间隔
     Row({ space: vp(10) }) {
       ForEach(this.codeKids, (item: string, index: number) => {
         TextInput()
           .backgroundColor("#CCFFFFFF") // 设置文本输入框的背景颜色
           .borderRadius(10) // 设置文本输入框的圆角
           .maxLength(1) // 设置最大输入长度为1
           .layoutWeight(1) // 设置布局权重
           .fontSize(25) // 设置字体大小
           .height("100%") // 设置高度为100%
           .type(InputType.Number) // 设置输入类型为数字
           .align(Alignment.Center) // 设置文本居中对齐
           .key(`code${index}`) // 为每个输入框设置唯一的键
           .onChange((value) => {
             if (value.length <= 1) {
               this.codeKids[index] = value // 存储用户输入的字符
             }
             if (index - 1 < this.codeKids.length) {
               let nextIndex = index + 1
               // 将焦点自动移动到下一个输入框
               focusControl.requestFocus(`code${nextIndex}`)
             } else {
               // 触发验证码完成回调函数
               this.inputResultCallback(this.codeKids.join(""))
             }
           })
       }, (item: string) => item)
     }
     .backgroundColor(Color.Black) // 设置整个行的背景颜色为黑色
     .height(80) // 设置行的高度为80
   }
 }

在新的代码中

  1. inputResultCallback属性:新增了一个名为 inputResultCallback 的属性,用于在用户完成输入后将结果传递给父组件。

  2. TextInput的onChange事件:在每个 TextInput 组件中添加了 onChange 事件处理程序。当用户输入内容时,这个事件处理程序会被触发。在事件处理程序内部,会进行以下操作:

    • 检查输入的值长度是否小于等于1,如果是则将该值存储在 codeKids 数组的相应位置上,以保证每个输入框只能输入一个字符。
    • 检查是否还有下一个输入框(index + 1 是否小于 codeKids 数组的长度)。如果有下一个输入框,将焦点自动移动到下一个输入框,以方便用户连续输入。
    • 如果没有下一个输入框,触发 inputResultCallback 回调函数,将输入的值传递给父组件或其他调用者。
  3. key属性:为每个 TextInput 组件添加了 key 属性,以确保focusControl.requestFocus的正确触发,这里我们使用了 index 来生成唯一的键。

三、奇怪的问题

  1. 输入框没有焦点

    第一次初始化的时候并没有获取焦点,系统也不知道焦点给谁。

    我们只需要在TextInput中加入

 .defaultFocus(index == 0)
  1. 删除onChange方法并不会触发

    整个流程都已经完成了,包括删除验证码!

 if (value.length <= 1) {
    this.codeKids[index] = value 
 }
这段代码赋予了当被删除的时候,数组中的值也会正确的改变。但是!

令人奇怪的是,在当前版本中当进行删除操作的时候,onChange方法并不会触发(平板、模拟器、手机均不会),所以我们需要另寻它法。

监听onKeyEvent!
 .onKeyEvent((event)=>{
   if (event.keyCode == KeyCode.KEYCODE_DEL) {
     
   }
 })

事实上,想法是美好的,这个方法也不会触发(模拟器、平板不触发、手机触发异常)

  1. 软键盘显示异常
 focusControl.requestFocus(nextKeyStr)
使用requestFocus的确可以将焦点切换到下一个输入框,但是软键盘确收起来了!

在这里我试了很多种办法。都没法做到尽善尽美。

多方查证,也觉得TextInput来做这个应该是不可行的,只能等官方下场修复。

那怎么办呢?

四、反过来想 Text() + TextInput()

如果多个输入框有问题,那么我用一个输入框不就行了?于是我就想到了使用多个Text(),一个TextInput的方案。

多个Text()用于排列显示,TextInput用于处理输入

只要显示正常,感知正常,那就没人知道怎么输入进去的~

 @Preview
 @Component
 struct CodeInputView {
   // 用于存储用户输入的字符的数组,初始值为5个空字符串
   @State codeKids: Array<string> = new Array(5).fill('')
 ​
   // 回调函数,用于传递输入结果给父组件
   inputResultCallback: (string) => void
 ​
   build() {
     // 使用 Stack 布局组织界面元素
     Stack() {
       if (this.codeKids != null) {
         // 创建一个横向排列的行,每个字符之间有一定的间隔
         Row({ space: vp(10) }) {
           // 使用 ForEach 循环遍历 codeKids 数组
           ForEach(this.codeKids, (item: string, index: number) => {
             // 显示用户输入的字符
             Text(item)
               .backgroundColor($r('app.color.white_80')) // 设置背景颜色
               .height(match()) // 设置高度匹配内容
               .layoutWeight(1) // 设置布局权重
               .fontSize(fp(25)) // 设置字体大小
               .textAlign(TextAlign.Center) // 设置文本水平居中对齐
               .align(Alignment.Center) // 设置垂直居中对齐
               .borderRadius(vp(15)) // 设置圆角
               .focusable(false) // 不可获得焦点
               .defaultFocus(false) // 默认不获得焦点
               .focusOnTouch(false) // 不在触摸时获得焦点
           }, (item: string) => item)
         }
         .height(match()) // 设置行的高度匹配内容
         .width(match()) // 设置行的宽度匹配内容
 ​
         // 创建一个输入框用于用户输入
         TextInput()
           .maxLength(this.viewSize) // 设置最大输入长度
           .fontSize(fp(25)) // 设置字体大小
           .borderRadius(vp(15)) // 设置圆角
           .type(InputType.Number) // 设置输入类型为数字
           .key(this.inputKey) // 设置唯一的键
           .onChange((value) => {
             // 将输入的字符拆分并分别显示在 Text 组件中
             let a = value.split('')
             this.codeKids.forEach((value, index) => {
               this.codeKids[index] = a[index] || ''
             })
             if (a.length >= this.viewSize) {
               // 当达到验证码长度时,触发回调函数传递输入结果
               this.inputResultCallback(value)
             }
             // 控制光标显示/隐藏
             this.showCaret = (a.length == 0)
           })
           .copyOption(CopyOptions.None) // 禁用复制操作
           .caretColor(this.showCaret ? Color.Black : Color.Transparent) // 设置光标颜色
           .fontColor(Color.Transparent) // 设置文本颜色为透明
           .backgroundColor(Color.Transparent) // 设置背景颜色为透明
           .height(match()) // 设置高度匹配内容
           .width(match()) // 设置宽度匹配内容
       }
     }
     .height(vp(80)) // 设置整个 Stack 的高度
   }
 }
  1. TextInput填充布局,置于顶层。文字和背景设置为透明,隐藏光标
 .copyOption(CopyOptions.None) // 禁用复制操作
 .caretColor(Color.Transparent) // 设置光标为透明
 .fontColor(Color.Transparent) // 设置文本颜色为透明
 .backgroundColor(Color.Transparent) // 设置背景颜色为透明
  1. 添加对应数量的Text,用作显示验证码。这一步其实就是将之前的ForEach中添加的TextInput换为Text即可

  2. 在onChange中分隔字符串,并存入对应下标的数组中

 // 将输入的字符拆分并分别显示在 Text 组件中
 let a = value.split('')
 this.codeKids.forEach((value, index) => {
   this.codeKids[index] = a[index] || ''
 })
 if (a.length >= this.viewSize) {
   // 当达到验证码长度时,触发回调函数传递输入结果
   this.inputResultCallback(value)
 }

使用也很简单

 CodeInputView({inputResultCallback: (code) => {
   //做点什么
 })

最终效果如下

五、最后

只需要稍微的封装下,将输入框的宽度、高度、圆角、颜色、输入类型、数量等包裹在一个对象中,使用@State修饰,并一一对应应用,即可将这个组件做成一个很标准的任意发挥的输入框啦。

唯一的遗憾是,目前没法去除TextInput点击的样式,除非你是纯色(纯色变化看不出来…)

以下就是该例子代码啦:

 @Preview
 @Component
 export struct CodeInputView {
   @State viewSize: number = 4
   inputResultCallback: (string) => void
   @Link codeKids: Array<string>
   @State showCaret: boolean = true
   private inputKey = "code_input"
 ​
   aboutToAppear() {
     if (this.codeKids == null) {
       this.codeKids = new Array(this.viewSize).fill('');
     }
   }
 ​
   build() {
     Stack() {
       if (this.codeKids != null) {
         Row({ space: vp(10) }) {
           ForEach(this.codeKids, (item: string, index: number) => {
             Text(item)
               .backgroundColor($r('app.color.white_80'))
               .height(match())
               .layoutWeight(1)
               .fontSize(fp(25))
               .textAlign(TextAlign.Center)
               .align(Alignment.Center)
               .borderRadius(vp(15))
               .focusable(false)
               .defaultFocus(false)
               .focusOnTouch(false)
               .onClick(() => {
                 focusControl.requestFocus(this.inputKey)
               })
           }, (item: string) => item)
         }
         .height(match())
         .width(match())
 ​
         TextInput()
           .maxLength(this.viewSize)
           .fontSize(fp(25))
           .borderRadius(vp(15))
           .type(InputType.Number)
           .key(this.inputKey)
           .onChange((value) => {
             let a = value.split('')
             this.codeKids.forEach((value, index) => {
               this.codeKids[index] = a[index] || ''
             })
             if (a.length >= this.viewSize) {
               this.inputResultCallback(value)
             }
             this.showCaret = (a.length == 0)
           })
           .copyOption(CopyOptions.None)
           .caretColor(this.showCaret ? Color.Black : Color.Transparent)
           .fontColor(Color.Transparent)
           .backgroundColor(Color.Transparent)
             //TODO 系统问题,如果背景色是透明的也没用,非透明可以
             // .stateStyles({ pressed: {.backgroundColor("跟背景一样的颜色(纯透明会黑色闪一下)")}})
           .height(match())
           .width(match())
       }
     }
     .height(vp(80))
   }
 }

六、总结

这个需求大概就告一段了,如果各位有什么想加的功能啥的,可以在评论区告知哦。

总之,HarmonyOS ArkUI的文档还是太少了,很多API都需要摸索,很多写法、操作都不习惯。以及很多坑!,Android的思维不适用在HarmonyOS。

为了能让大家更好的学习鸿蒙 (OpenHarmony) 开发技术,这边特意整理了《鸿蒙 (OpenHarmony)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (OpenHarmony)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. ……

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门?:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/391733.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++| VS+QT快速入门

VSQT C开发QT的方式VSQT和QT Creater的区别VSQT使用&#xff1a;入门案例——加法器创建项目UI文件运行项目编辑UI界面代码交互编写中文显示乱码 C开发QT的方式 C开发QT有两种方式&#xff0c;一种是VSQT&#xff0c;还有一种是QT Creater。 QT Creater的用法已经在C学习| QT…

Java Lambda表达式:简化编程,提高效率

Java Lambda表达式&#xff1a;简化编程&#xff0c;提高效率 1. 使用Lambda表达式进行集合遍历1.1 未使用Lambda表达式&#xff1a;1.2 使用Lambda表达式&#xff1a; 2. 使用Lambda表达式进行排序2.1 未使用Lambda表达式&#xff1a;2.2 使用Lambda表达式&#xff1a; 3. 使用…

Sora:将文本转化为视频的创新之旅

一.能力 我们正致力于让 AI 掌握理解和模拟物理世界动态的能力&#xff0c;旨在培养能够协助人们解决现实世界互动问题的模型。 介绍 Sora——我们开发的文本到视频转换模型。Sora 能够根据用户的输入提示&#xff0c;生成最长达一分钟的高质量视频内容。 目前&#xff0c;Sora…

MCU中断控制

目录 一、中断相关基础知识 1、NVIC&#xff1a;嵌套向量中断控制器 2、可屏蔽中断和不可屏蔽中断的区别 3、中断优先级 4、常见特殊中断 二、中断相关寄存器 三、中断使用步骤&#xff1a; 一、中断相关基础知识 1、NVIC&#xff1a;嵌套向量中断控制器 (1) 它是内核的…

1.8 NLP自然语言处理

NLP自然语言处理 更多内容&#xff0c;请关注&#xff1a; github&#xff1a;https://github.com/gotonote/Autopilot-Notes.git 一、简介 seq2seq(Sequence to Sequence)是一种输入不定长序列&#xff0c;产生不定长序列的模型&#xff0c;典型的处理任务是机器翻译&#…

2.15练习

选择题 1. B2. B3. C4. D5. A6. B7. C8. B9. D10. B11. A12. B 填空题 1. a b c 2.string s: I like it. String t:A like it. 3. 30 10 30 n Learning

【复合多尺度熵与特征提取】一文看懂“复合多尺度熵”——复合多尺度样本熵、模糊熵、排列熵、包络熵、功率谱熵、能量熵、奇异谱熵及其MATLAB实现

在上一篇文章中&#xff0c;我们讲了多尺度熵的原理及MATLAB实现。 本篇要讲的是多尺度熵的一个改进特征提取方法——复合多尺度熵&#xff08;Composite Multiscale Entropy, CMSE&#xff09;。复合多尺度熵方法不仅继承了多尺度熵在揭示时间序列多尺度复杂性方面的优势&…

力扣题目训练(12)

2024年2月5日力扣题目训练 2024年2月5日力扣题目训练476. 数字的补数482. 密钥格式化485. 最大连续 1 的个数148. 排序链表164. 最大间距 2024年2月5日力扣题目训练 2024年2月5日第十二天编程训练&#xff0c;今天主要是进行一些题训练&#xff0c;包括简单题3道、中等题2道和…

c高级day4作业

终端输入一个字符&#xff0c;判断是大写字母小写字母还是数字字符。 #!/bin/bash read -p "input字符--->" a case $a in[[:upper:]])echo 大写字母$a;;[[:lower:]])echo 小写字母$a;;[0-9])echo 数字字符$a;;*)echo "error" esac终端输入年月&#x…

GIS 基于 MCDM-AHP 方法研究潜在风力发电厂区域

随着全球人口的迅速增长、现有不可再生能源的不足以及工业的快速发展,人们对可再生能源的兴趣与日俱增。除了化石燃料的有限供应外,由于无法避免其对环境造成的破坏,人们开始转向替代能源。风能是最具商业价值的能源之一,既环保又可持续。然而,为了使风力发电厂发挥最大效…

⭐北邮复试刷题103. 二叉树的锯齿形层序遍历

103. 二叉树的锯齿形层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的 锯齿形层序遍历 。&#xff08;即先从左往右&#xff0c;再从右往左进行下一层遍历&#xff0c;以此类推&#xff0c;层与层之间交替进行&#xff09;。 示例 1&#xff1a;输入&#xff1a…

链式结构实现队列

链式结构实现队列 1.队列1.1队列的概念及结构1.2队列的实现 2. 队列的各种函数实现3. 队列的全部代码实现 1.队列 1.1队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(Fi…

深入解析域名短链接生成原理及其在Python/Flask中的实现策略:一篇全面的指南与代码示例

为了构建一个高效且用户友好的域名短链服务&#xff0c;我们可以将项目精简为以下核心功能板块&#xff1a; 1. 用户管理 注册与登录&#xff1a;允许用户创建账户并登录系统。 这部分内容可以参考另一片文章实现&#xff1a; 快速实现用户认证&#xff1a;使用Python和Flask…

Aster实现一台电脑当两台使——副屏使用独立win账号

前言&#xff1a;笔者每年回家&#xff0c;都面临着想要和小伙伴一起玩游戏&#xff0c;但小伙伴没有电脑/只有低配电脑的问题。与此同时&#xff0c;笔者自身的电脑是高配置的电脑&#xff0c;因此笔者想到&#xff0c;能否在自己的电脑上运行游戏&#xff0c;在小伙伴的电脑上…

得物面试:Redis用哈希槽,而不是一致性哈希,为什么?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; Redis为何用哈希槽而不用一致性哈希&#xff1f; 最近…

智能传感器阅读笔记-智能传感器的发展历程、发展趋势及方向

智能传感器的发展历程 第一代智能传感器 第一代智能传感器是数字式传感器&#xff0c;指改造A/D转换模块&#xff0c;并采用数字技术进行信号处理&#xff0c;使输出信号为数字信号&#xff08;或数字编码&#xff09;的传感器&#xff0c;主要由放大器、A/D转换模块、微处理…

解决STM32MP157开发板密码登录问题

开发板密码登录问题是很多人遇到的问题&#xff0c;网上有很多帖子&#xff0c;我也参考过&#xff0c;不太适用&#xff0c;很复杂&#xff0c;甚至会被误导&#xff0c;我差点连ubuntu虚拟机都无法登录了。有的密码匹配&#xff0c;有的取消不了密码。 1、密码配置&#xff…

ABC341 A-F

Toyota Programming Contest 2024#2&#xff08;AtCoder Beginner Contest 341&#xff09; - AtCoder B读不懂题卡了&#xff0c;F读假题卡了&#xff0c;开题开慢了rank了 A - Print 341 题意&#xff1a; 打印一串交替出现的包含N个0&#xff0c;N1个1的01串 代码&…

【案例8】用户中心实现涉及内容和过程

图1 如图1是用盒子模型内容实现的&#xff0c;但是需要了解一些内容。 一.内容知识引入 1.内边距属性&#xff08;padding&#xff09; 为了调整盒子在网页中的显示位置&#xff0c;常常需要为元素设置内边距。内边距也被称为内填充&#xff0c;是指元素内容和边框之间的距离…

Windows程序互斥锁 - 一个程序同时仅允许运行一个实例

Windows程序互斥锁 - 一个程序同时仅允许运行一个实例 前言 鉴于应用逻辑需要&#xff0c;有些Windows应用同时只能运行一个实例。例如&#xff1a;一个电脑只能同时运行一个微信&#xff08;手速快了当我没说&#xff0c;不信你去试试&#xff09;。 怎么实现呢&#xff1f…