前文
在开发鸿蒙系统应用时,自定义PIN码输入框和数字键盘的需求在许多场景中非常常见,尤其是在涉及到安全输入或数字验证的场景中,比如支付验证、账户登录等。本文将详细解读如何实现一个自定义的PIN码输入框和数字键盘,先上效果图:
安装
ohpm install @pharos/pin_code_text_input
使用
import { PinCodeTextInput } from '@pharos/pin_code_text_input';
PinCodeTextInput({
widgetWidth: '80%', // 宽度
boxCount: 6, // 输入框个数
enableClose: true, // 启用关闭按钮
onInputCompleted: async (inputArray: Array<string>) => {
const inputValue = inputArray.join(';')
console.info('inputValue <<< ', inputValue)
}, // 输入完毕
onInputIntercept: async () => {
return false
}, // 是否拦截输入
isPassword: false, // 是否密码输入, 若 true 输入框内容为遮掩内容
isRequestFocus: true, // 是否获取焦点, 若true则弹出键盘
enableKeyboardOnFocus: true, // 当焦点时, 是否弹出键盘
safeAreaPadding: {
bottom: 20,
}, // 键盘的安全区域
keyboardHeight: 270, // 键盘高度
keyboardSupportAvoidance: true, // 键盘遮挡时,输入框是否自动避让
keyboardTitleLabel: 'DIGIT KEYBOARD', // 键盘标题
})
PIN码输入框的实现
PIN码输入框组件PinCodeTextInput负责显示用户输入的内容,并根据设定显示为普通数字或密文(如*)。该组件允许用户设置多个参数,以适应不同的应用场景,包括输入框的宽度、格子数量、是否显示密文、键盘高度等。
核心属性
@Prop widgetWidth: 定义输入框的宽度。@Prop boxCount: 输入框格子数量,默认是6。@Prop isPassword:是否以密文形式显示PIN码。@Prop enableKeyboardOnFocus: 是否在输入框获得焦点时自动唤起键盘。@Prop keyboardHeight: 定义数字键盘的高度。@State caretIndex: 当前光标的位置。@State textValueArray: 保存用户的输入内容。
构建输入框格子
通过buildBox方法,我们为每一个输入格子构建UI,并根据是否是密文来显示内容。在显示光标时,使用了一个闪烁的动画效果,使输入体验更加真实。
@Builder
buildBox(index: number, first?: boolean) {
Stack() {
Column()
.opacity(this.isCaretOpacity ? 1 : 0)
.animation({
curve: curves.springMotion(),
duration: 300,
})
.visibility(this.caretIndex === index ? Visibility.Visible : Visibility.None)
.width(1)
.height(20)
.backgroundColor('#60999999')
if (this.isPassword) {
// 密文显示为 '*'
Flex({
justifyContent: FlexAlign.Center,
alignItems: ItemAlign.Center,
}) {
Column()
.borderRadius(45 / 2)
.width(45 / 2)
.height(45 / 2)
.backgroundColor('#333')
.visibility(this.getValue(index) === '*' ? Visibility.Visible : Visibility.Hidden)
}
} else {
// 显示实际输入的数字
Text(this.getValue(index)).fontSize(24)
.lineHeight('100%')
.textAlign(TextAlign.Center)
}
}
.height('100%')
.width(`calc(100% / ${this.boxCount})`)
.border({
width: {
top: 1,
left: first ? 1 : 0,
bottom: 1,
right: 1
},
color: '#46333333'
}).flexGrow(1)
}
光标闪烁效果
通过定时器控制光标的透明度,实现光标的闪烁效果。这个定时器每600毫秒切换一次光标的透明度,使用户在输入时有明确的视觉提示。
setCaretOpacity() {
this.isCaretOpacity = !this.isCaretOpacity
this.timeoutId = setTimeout(() => {
this.setCaretOpacity()
}, 600)
}
数字键盘的实现
自定义键盘的功能
数字键盘的构建通过ProxyPinCodeTextInput组件来完成。该组件允许自定义键盘的外观与行为,包括键盘的高度、是否显示关闭按钮等。KeyboardBuilder方法负责构建数字键盘,并处理用户点击数字键或删除键的事件。
@Builder
KeyboardBuilder() {
Keyboard({
titleLabel: this.keyboardTitleLabel,
keyboardType: this.keyboardType,
enableClose: this.enableClose,
onTapClose: () => {
this.controller.stopEditing()
},
onTapKey: async (type, value) => {
const block = await this.onInputIntercept().catch(() => false)
if (block === true) {
if (this.textValueArray.length == this.boxCount) {
this.textValueArray = []
}
return
}
if (type === KeyType.DEL && this.textValueArray.length > 0) {
this.textValueArray.pop()
this.textValueArray = [...this.textValueArray]
} else if (type === KeyType.VALUE && value && this.textValueArray.length < this.boxCount) {
this.textValueArray.push(value)
if (this.textValueArray.length == this.boxCount) {
await this.onInputCompleted([...this.textValueArray]).catch(() => {
})
this.textValueArray = []
}
}
},
})
.height(this.keyboardHeight)
.padding(this.safeAreaPadding || { bottom: 20 })
.backgroundColor('#FFF0F0F0')
}
TextInput()
.customKeyboard(this.KeyboardBuilder(), {
supportAvoidance: this.keyboardSupportAvoidance
})
键盘输入逻辑
用户点击数字键时,数字会被添加到textValueArray中。
当输入达到boxCount(默认6个字符)时,会触发onInputCompleted事件,表示输入已完成。
用户可以使用删除键删除最后一位输入。
其他特性
输入拦截
通过onInputIntercept方法,可以控制是否允许继续输入。在某些情况下,例如账户被锁定或者达到最大重试次数时,可以通过拦截机制阻止用户输入。
粘贴功能
我们为输入框添加了粘贴功能,允许用户直接将文本粘贴到输入框中。如果文本符合条件(如非密码模式下只允许数字),系统会自动将其拆分并填充到输入框中。
.onPaste(async res => {
if (this.isPassword !== true) {
this.textValueArray = (res || '').replace(/\D/g, '').split('')
if (this.textValueArray.length == this.boxCount) {
await this.onInputCompleted([...this.textValueArray]).catch(() => {
})
this.textValueArray = []
}
}
})