需求背景:需要做一个多输入框的验证码模块,输入验证码时输入后光标会自动跳转至下一个输入框,删除验证码时会自动删除上一个输入框内容,并且光标跳转至上一个输入框内。6位验证码全部输完后进行登录请求
具体样式如下图:
具体实现效果如下图:
思路分析:
1.组件化
这个验证码输入框每个都样式和交互逻辑都非常相似,将其提取出作为组件化编写,减少代码冗余,先将单个输入框的样式进行编写
ArkTs
@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配合使用进行切换聚焦组件,切换焦点
ArkTs
// 自定义输入框组件
@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的值递减来控制光标的移动,而每次删除时,都会往前跳两个输入框,并且输入框的内容不会删除,所以由此我想到可以一次删除方法来进行光标跳转,一次删除方法用来删除输入框的内容。
以下为更新后最终代码
ArkTs
@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)
}
}
// 页面代码
// 页面代码与第2点相同,在此不过多赘述
此次在codeInputView组件的初始化中code值有进行修改,添加了''空字符串,这是因为通过打印日志发现,如果不进行赋值的初始化时,code值会为undefined,这个在删除code值时会非常奇怪,所以进行了设空字符串进行初始化
最终即可实现需求所要求的交互逻辑
当前HarmonyOs仍在初步学习过程中,大家如果感兴趣或者有问题可以一起沟通交流,目前在学习过程中也遇到一些HarmonyOs开发组件上的一些小bug,希望华为可以及时进行更新修复!努力并大步的发展我们国家自己的移动端系统,遥遥领先!
如果该文章对你有所帮助的话,可以点赞、收藏并关注一下!后续会持续更新更多技术内容