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

一、前言

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

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

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

二、ForEach + TextInput

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

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

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

 @Preview@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentexport struct CodeInputView {@State viewSize: number = 4inputResultCallback: (string) => void@Link codeKids: Array<string>@State showCaret: boolean = trueprivate 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.……

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity 2D Spine 外发光实现思路

Unity 2D Spine 外发光实现思路 前言 对于3D骨骼&#xff0c;要做外发光可以之间通过向法线方向延申来实现。 但是对于2D骨骼&#xff0c;各顶点的法线没有向3D骨骼那样拥有垂直于面的特性&#xff0c;那我们如何做2D骨骼的外发光效果呢&#xff1f; 理论基础 我们要知道&a…

书生·浦语-模型评测opencompass

大预言模型评测 模型评测包括主管评测与客观评测 测试模型对提示词的敏感性&#xff0c;或通过提示词获得更准确地答案 主流评测框架 opencompass评测平台 作业

智慧公厕:让智慧城市的公共厕所焕发“智慧活力”

智慧城市的建设已经进入了一个新的阶段&#xff0c;不仅仅是智慧交通、智慧环保&#xff0c;如今甚至连公厕都开始迎来智慧化时代。智慧公厕作为智慧城市的神经末梢&#xff0c;正在通过信息化、数字化和智慧化的方式&#xff0c;实现全方位的精细化管理。本文以智慧公厕源头专…

Flink理论—Flink架构设计

Flink架构设计 Flink 是一个分布式系统&#xff0c;需要有效分配和管理计算资源才能执行流应用程序。它集成了所有常见的集群资源管理器&#xff0c;例如Hadoop YARN&#xff0c;但也可以设置作为独立集群甚至库运行,例如Spark 的 Standalone Mode 本节概述了 Flink 架构&…

初识最短路径

一.最短路径的介绍 最短路径是图论和网络分析中一个重要的概念&#xff0c;它指的是在一个图或网络中连接两个节点或顶点的路径中&#xff0c;具有最小权重总和的路径。这个权重可以表示为路径上边或弧的长度、耗费、时间等&#xff0c;具体取决于问题的背景和应用场景。 如果你…

【STM32 CubeMX】SPI层次结构SPI协议与SPI控制器结构

文章目录 前言一、SPI 程序层次1.1 硬件原理图1.2 硬件框图1.3 软件层次 二、SPI协议2.1 硬件连线2.2 如何访问SPI设备2.3 SPI 框图 总结 前言 随着嵌入式系统的迅猛发展&#xff0c;STM32系列微控制器在各种应用中得到广泛应用。在嵌入式系统设计中&#xff0c;串行外设接口&…

牛客网SQL进阶123:高难度试卷的得分的截断平均值

官网链接&#xff1a; SQL类别高难度试卷得分的截断平均值_牛客题霸_牛客网牛客的运营同学想要查看大家在SQL类别中高难度试卷的得分情况。 请你帮她从exam_。题目来自【牛客题霸】https://www.nowcoder.com/practice/a690f76a718242fd80757115d305be45?tpId240&tqId2180…

matplotlib从起点出发(13)_Tutorial_13_Autoscaling

0 自动放缩 轴上的限制可以手动设置&#xff08;例如ax.set_xlim(xmin, xmax))&#xff0c;或者Matplotlib可以根据Axes上已有的数据自动设置它们。此种放缩行为有许多选项&#xff0c;如下所述。 我们将从一个简单的折线图开始&#xff0c;显示自动缩放将轴限制扩展到数据的…

P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G题解

题目 在一个果园里&#xff0c;多多已经将所有的果子打了下来&#xff0c;而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。 每一次合并&#xff0c;多多可以把两堆果子合并到一起&#xff0c;消耗的体力等于两堆果子的重量之和。可以看出&#xff0c;所…

绿色化 数据库 MongoDB 和 mysql 安装

绿色化 数据库 MongoDB 和 mysql 安装 【1.1】 前言 为什么要绿色化 安装呢&#xff1f;因为系统老升级&#xff0c;老重装&#xff01;&#xff01;也方便了解下数据库配置和库在那 绿色软件喜欢一般放在 D盘tools目录里 D:\tools\ 数据库 MongoDB D:\tools\MongoDB 数…

Mysql第二关之存储引擎

简介 所有关于Mysql数据库优化的介绍仿佛都有存储引擎的身影。本文介绍Mysql常用的有MyISAM存储引擎和Innodb存储引擎&#xff0c;还有常见的索引。 Mysql有两种常见的存储引擎&#xff0c;MyISAM和Innodb&#xff0c;它们各有优劣&#xff0c;经过多次优化和迭代&#xff0c;…

代码随想录算法训练营第十九天|654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

654.最大二叉树 刷题https://leetcode.cn/problems/maximum-binary-tree/description/文章讲解https://programmercarl.com/0654.%E6%9C%80%E5%A4%A7%E4%BA%8C%E5%8F%89%E6%A0%91.html视频讲解https://www.bilibili.com/video/BV1MG411G7ox/?vd_sourceaf4853e80f89e28094a5fe…

软件工程师,OpenAI Sora驾到,快来围观

概述 近期&#xff0c;OpenAI在其官方网站上公布了Sora文生视频模型的详细信息&#xff0c;展示了其令人印象深刻的能力&#xff0c;包括根据文本输入快速生成长达一分钟的高清视频。Sora的强大之处在于其能够根据文本描述&#xff0c;生成长达60秒的视频&#xff0c;其中包含&…

1、若依(前后端分离)框架的使用

若依&#xff08;前后端分离&#xff09;框架的使用 0、环境1、下载若依(1) 下载并解压(2) 导入SQL语句(3) 配置Redis、MySQL 2、运行若依3、登录(1) 前端(2) 后端 4、获取用户角色、权限和动态路由(1) 获取用户角色、权限(2) 根据用户信息获取动态路由【getRouters】 5、杂6、…

阿里云服务器ECS租赁费用报价_CPU内存_带宽和系统盘价格表

2024年最新阿里云服务器租用费用优惠价格表&#xff0c;轻量2核2G3M带宽轻量服务器一年61元&#xff0c;折合5元1个月&#xff0c;新老用户同享99元一年服务器&#xff0c;2核4G5M服务器ECS优惠价199元一年&#xff0c;2核4G4M轻量服务器165元一年&#xff0c;2核4G服务器30元3…

解线性方程组(二)——Jacobi迭代法求解(C++)

迭代法 相比于直接法求解&#xff0c;迭代法使用多次迭代来逐渐逼近解&#xff0c;其精度比不上直接法&#xff0c;但是其速度会比直接法快很多&#xff0c;计算精度可控&#xff0c;特别适用于求解系数矩阵为大型稀疏矩阵的方程组。 Jacobi迭代法 假设有方程组如下&#xf…

QGIS004:【08图层工具箱】-导出到电子表格、提取图层范围

摘要&#xff1a;QGIS图层工具箱常用工具有导出到电子表格、提取图层范围等选项&#xff0c;本文介绍各选项的基本操作。 实验数据&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ZK4_ShrQ5BsbyWfJ6fVW4A?pwdpiap 提取码&#xff1a;piap 一、导出到电子表格 工具…

OpenAl 视频生成模型 —— Sora技术报告解读

这里是陌小北&#xff0c;一个正在研究硅基生命的碳基生命。正在努力成为写代码的里面背诗最多的&#xff0c;背诗的里面最会写段子的&#xff0c;写段子的里面代码写得最好的…厨子。 写在前面 早上醒来&#xff0c;就看到OpenAl推出的视频模型Sora炸锅了&#xff0c;感觉所…

代码随想录 Leetcode452. 用最少数量的箭引爆气球

题目9&#xff1a; 代码&#xff08;首刷看解析 2024年2月17日&#xff09;&#xff1a; class Solution { private:const static bool cmp(vector<int>& a, vector<int>& b) {return a[0] < b[0];} public:int findMinArrowShots(vector<vector<…

Linux下解压tar.xz文件的命令

tar -c: 建立压缩档案-x&#xff1a;解压-t&#xff1a;查看内容-r&#xff1a;向压缩归档文件末尾追加文件-u&#xff1a;更新原压缩包中的文件 ------------------------------------------ 这五个是独立的命令&#xff0c;压缩解压都要用到其中一个&#xff0c;可以和别的…