场景描述
场景一:英文、数字及特殊字符和中文字符自动间隔一个空格的距离场景二:当输入的内容(纯数字可能为小数)大于999时,禁止输入,注意最后一位光标不要闪烁场景三:限制两位小数,不能以小数点开头的场景场景四:TextInput如何在输入、粘贴、剪切动作之后,内容显示之前控制显示内容场景五:电话号码格式化时,删除号码中间的空格为删除前一位数字场景六:textInput防抖与节流
场景一:TextInput实现输入框热搜词自动滚动及右侧文字内容颜色渐变
效果图:
方案:
1、封装一个正则函数,英文数字及特殊字符用[a-zA-Z0-9\~!@\#$%^&\*\(\)\\-\_=+\\\[\\\]\{\}\\\\|;:'",<.\>/?\],中文用\[\\u4e00-\\u9fa5\], 在正则中$1 和 $2 是反向引用,它们用于引用正则表达式中捕获组匹配到的文本,捕获组是用圆括号 \(\) , 每个捕获组匹配到的文本可以在替换字符串中通过 $1 和 $2 等来引用。
// 场景1 英文、数字及特殊字符和中文字符自动间隔空格的距离
formatStringOne(value: string) {
// 中文与英文或者特殊字符空格 /g全局
// 每个捕获组匹配到的文本可以在替换字符串中通过 $1、$2 等来引用。
let pattern = /([\u4e00-\u9fa5])([a-zA-Z0-9`~!@#$%^&*()\-_=+\[\]{}\\|;:'",<.>/?])/g;
let result = value.replace(pattern, '$1 $2');
let pattern1 = /([a-zA-Z0-9`~!@#$%^&*()\-_=+\[\]{}\\|;:'",<.>/?])([\u4e00-\u9fa5])/g;
let result1 = result.replace(pattern1, '$1 $2');
return result1;
}
2、在onChange回调中调用封装的正则匹配函数formatStringOne。
TextInput({ text: this.textOne, placeholder: 'input your word ...', controller: this.controllerOne })
.onChange((value:string)=>{
this.textOne = this.formatStringOne(value)
})
场景二:当输入的内容(纯数字可能为小数)大于999时,禁止输入,注意最后一位光标不要闪烁
效果图:
方案:
1、onWillInsert是在将要输入时调用的回调。在返回true时,表示正常插入,返回false时,表示不插入。在预上屏操作时,该回调不触发。注意:仅支持系统输入法输入的场景。如果在onChange中判断会先显示最后一位输入的字符,然后判断不合法后删除,时机有些滞后,这里需要在字符显示之前拦截掉,所以考虑用onWillInsert。2、在onWillInsert中对输入之前对内容进行判断,当输入框内容大于等于999且将要插入的索引大于等于3,或者输入框内容长度等于3且内容不包含'.'将要输入的内容也非'.',则返回false,不让输入。
TextInput({ text: this.textTwo, placeholder: 'input your word...', controller: this.controllerTwo })
.type(InputType.NUMBER_DECIMAL)// 带小数点的数字输入模式。
.onChange((value: string) => {
if (value === '.') {
this.textTwo = '0.';
} else {
this.textTwo = value;
}
})
.onWillInsert((info: InsertValue) => {
if ((Number(this.textTwo) >= 999 && info.insertOffset >= 3) ||
this.textTwo.length === 3 && info.insertValue !== '.' && this.textTwo.includes('.')===false) {
return false
}
return true
})
场景三:限制两位小数,不能以小数点开头的场景
效果图:
方案:
1、限制输入内容只能是两位小数,如在onChange中用正则判断输入内容是否符合要求,会导致光标有闪烁现象,根因同场景2因为onChange中判断时机滞后,这种场景需要在输入之前进行拦截2、在onChange中补齐0,如果直接输入.会在.之前补0.3、在onWillInsert中判断如果已输入的字符包含.且将要输入的字符是. 或如已输入的内容满足两位小数条件,返回false不让输入,否则返回true
TextInput({ text: this.textThree, placeholder: 'input your word...', controller: this.controllerThree })
.type(InputType.NUMBER_DECIMAL)
.onChange((value: string) => {
// 当直接输入‘.’的时候自动补齐‘0.’,否则正常显示输入的字符
if (value === '.') {
this.textThree = '0.';
} else {
this.textThree = value;
}
})
// 在返回true时,表示正常插入,返回false时,表示不插入
.onWillInsert((info: InsertValue) => {
// ^ 和 $ 分别指字符串的开始与结束,以数字开始,以小数点+两位数字结束
let reg = /^\d+\.\d{2}$/
// 如果输入的文本包含'.',且将要输入的字符是'.' 或者输入框的内容已经满足两位小数,返回false不让输入
if (this.textThree.includes('.') && info.insertValue === '.' || reg.test(this.textThree)) {
return false
}
return true
})
场景四:TextInput如何在输入、粘贴、剪切动作之后,内容显示之前控制显示内容
效果图:
方案:
1、封装一个格式化字符串的函数,限制输入字符串数量,且每隔固定字符空一格。
formatStringFour(value: string) {
let result = '';
let num = 0; // 计算每隔固定字符空一格
let message = ''; // 每固定字符为一段
for(let i=0;i<value.length;i++){
if(value[i] != ' '){
message += value[i]
num += 1
}
if(num == CHUNKSIZE){
result += message + ' '
num = 0
message = ''
}
}
result += message
result = result.slice(0, LIMIT);
return result;
}
2、通过记录下次光标设置的位置,设置光标的位置。
setCaret(controller:TextInputController) {
if (this.nextCaret != -1) {
console.log("to keep caret position right, change caret to", this.nextCaret)
controller.caretPosition(this.nextCaret)
this.nextCaret = -1
}
}
3、在onWillInsert中对输入框字符数量做限制,当大于输入限制的字符数或者输入内容为空时不让输入,通过onTextSelectionChange中记录光标的位置,然后在onPaste回调处理光标前后以及粘贴的文字,并通过格式化字符串函数过滤输入的内容。
4、封装一个记录光标位置的函数,传入onChange改变前去空格的字符以及改变后去空格的字符,通过判断改变前长度小于改变后长度为插入场景,反之为删除的场景。在onChange中判断如果数字已经格式化完成了,在这个时候通过setCaret改变光标位置不会被重置掉,否则用calcCaretPosition记录下次光标位置,等格式化完成后设置。
calcCaretPosition(text: string, nextText: string) {
let befNumberNoSpace: string = this.removeSpace(text)
this.actualCh = 0
if (befNumberNoSpace.length < this.numberNoSpace.length) { // 插入场景
for (let i = 0; i < this.selectionStart; i++) {
if (text[i] != ' ') {
this.actualCh += 1
}
}
this.actualCh += this.numberNoSpace.length - befNumberNoSpace.length
for (let i = 0; i < nextText.length; i++) {
if (nextText[i] != ' ') {
this.actualCh -= 1
if (this.actualCh <= 0) {
this.nextCaret = i + 1
break;
}
}
}
} else if (befNumberNoSpace.length > this.numberNoSpace.length) { // 删除场景
if (this.selectionStart === text.length) {
console.log("Caret at last, no need to change")
} else if (this.selectionStart === this.selectionEnd) {
// 按键盘上回退键一个一个删的情况
for (let i = this.selectionStart; i < text.length; i++) {
if (text[i] != ' ') {
this.actualCh += 1
}
}
for (let i = nextText.length - 1; i >= 0; i--) {
if (nextText[i] != ' ') {
this.actualCh -= 1
if (this.actualCh <= 0) {
this.nextCaret = i
break;
}
}
}
} else {
// 剪切/手柄选择 一次删多个字符
this.nextCaret = this.selectionStart // 保持光标位置
console.info(`chenbilian nextCaret:${this.nextCaret}`)
}
}
}
TextInput({ text: this.textFour, placeholder: 'input your word...', controller: this.controllerFour })
.onWillInsert((info: InsertValue) => {
console.log(`onWillInsert:${info.insertValue} ${info.insertOffset}`)
// 当输入的字符串数量大于限制或者输入空格,返回false
if (info.insertOffset >= LIMIT || info.insertValue === ' ' || this.textFour.length >= LIMIT) {
return false
}
return true;
})
.onChange((value: string) => {
this.numberNoSpace = this.removeSpace(value);
let nextText: string = ""
nextText = this.formatStringFour(value)
if (this.textFour === nextText && nextText === value) {
this.setCaret(this.controllerFour)
} else {
this.calcCaretPosition(this.textFour, nextText)
}
this.textFour = nextText
})
.onPaste((value: string, event: PasteEvent) => {
// 获取粘贴板内容
let clipboardText = value;
// 记录当前光标前后的内容
let textBeforeCursor = this.textFour.slice(0, this.selectionStart);
let textAfterCursor = this.textFour.slice(this.selectionEnd);
// 将粘贴的内容插入光标中间
this.textFour = textBeforeCursor + clipboardText + textAfterCursor;
// 通过条件过滤文本
this.textFour = this.formatStringFour(this.textFour)
})
.onTextSelectionChange((selectionStart, selectionEnd) => {
// 记录光标位置
this.selectionStart = selectionStart
this.selectionEnd = selectionEnd
})
场景五:电话号码格式化时,删除号码中间的空格为删除前一位数字
方案:
1、相对于官方文档示例6,主要是在onChange中对删除的场景做判断,如果上次输入框字符长度大于当前的字符长度判断为删除,反之为插入。
2、通过判断删除的字符串索引8-9或者3-4的地方是否为空字符,如果不是空字符可以判断是删除了空格,再通过removeCharAt函数对输入框字符串做截取,将空格前一位数字删掉。
3、在onChange中判断如果数字已经格式化完成了,在这个时候通过setCaret改变光标位置不会被重置掉,否则用calcCaretPosition记录下次光标位置,等格式化完成后设置。
//删除指定位置的字符
removeCharAt(str:string, index:number) {
if (index >= 0 && index < str.length) {
return str.slice(0, index) + str.slice(index + 1);
}
return str; // 如果索引超出范围,返回原字符串
}
TextInput({...})
.onChange((value:
this.teleNumberNoSpace = this.removeSpace(value);
console.log(':::number',value.substring(8, 9))
//判断 删除场景 去空字符串小于等于11 变动的字符串小于13 判断是否有特殊字符
if (this.textFive.length > value.length && this.teleNumberNoSpace.length <= 11
&& value.length<NUM_TEXT_MAXSIZE_LENGTH && this.checkNeedNumberSpace(value)) {
//判断是否删除了空白字符,用空格分隔字符串
let parts = value.split(' ')
console.info(`parts:${parts.length}`)
if (parts.length - 1 != 2) {
//删除第二个空白字符场景
if (value.substring(8, 9) != ' ') {
//012 4567 9012
value = this.removeCharAt(value,7)
}
//删除第一个空白字符场景
if (value.substring(3, 4) != ' ') {
//012 4567 9012
value = this.removeCharAt(value,2)
}
}
}
//更新去空后的电话号码
this.teleNumberNoSpace = this.removeSpace(value);
let nextText: string = ""
if (this.teleNumberNoSpace.length > NUM_TEXT_MAXSIZE_LENGTH - 2) {
nextText = this.teleNumberNoSpace
} else if (this.checkNeedNumberSpace(value)) {
if (this.teleNumberNoSpace.length <= 3) {
nextText = this.teleNumberNoSpace
} else {
let split1: string = this.teleNumberNoSpace.substring(0, 3)
let split2: string = this.teleNumberNoSpace.substring(3)
nextText = split1 + ' ' + split2
if (this.teleNumberNoSpace.length > 7) {
split2 = this.teleNumberNoSpace.substring(3, 7)
let split3: string = this.teleNumberNoSpace.substring(7)
nextText = split1 + ' ' + split2 + ' ' + split3
}
}
} else {
nextText = value
}
console.log("onChange Triggered:" + this.textFive + "|" + nextText + "|" + value)
if (this.textFive === nextText && nextText === value) {
// 此时说明数字已经格式化完成了 在这个时候改变光标位置不会被重置掉
console.info(`chenbilian nextCaret1:${this.nextCaret}`)
this.setCaret(this.controllerFive)
} else {
this.calcCaretPosition(this.textFive,nextText)
}
this.textFive = nextText
})
场景六:textInput防抖与节流
方案:
1、防抖:规定事件内只触发一次,在事件触发后等待一定的延迟时间,如果在这段延迟时间内事件再次被触发,则重新开始计时封装一个输入框的防抖函数,当频繁触发某个事件时会清空计时器重新计时,这样可以达到防抖的效果,我们可以将需要请求的数据放在debouncedChangeValue 回调中处理。
debouncedChangeValue = this.debounce(() => {
// 处理文本变化的逻辑,如请求搜索数据 300ms 的防抖时间
console.log('debouncedChangeValue:', this.textSix);
}, 300);
// 防抖函数
private debounce(func: () => void, delay: number) {
let timer: number | null = null; // 定时器
return () => {
if (timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func();
}, delay);
};
}
TextInput({ text: this.textSix, placeholder: 'input your word...', controller: this.controllerSix })
.onChange((value: string)=>{
this.textSix = value
// 在debouncedOnChange中处理发送请求等逻辑
this.debouncedChangeValue()
})
2、节流:当持续触发事件时,保证一定时间段内只调用一次事件处理函数,防抖节流函数可以根据具体业务需求使用。
throttleChangeValue = this.throttle(() => {
// 处理文本变化的逻辑,如请求搜索数据 1000ms 的节流时间
console.info(`ChangeValue:${this.changeValue}`)
}, 1000);
// 节流函数
private throttle(fn: () => void, delay:number) {
let timer : number | null = null; // 定时器
let lastTime = 0 // 上一次执行时间
return () => {
const nowTime =Number(new Date()) // 获取当前时间
const remainingTime = delay-(nowTime-lastTime) // 剩余时间
console.info(`chen---nowTime:${nowTime}---remainingTime:${remainingTime}`)
if(remainingTime <= 0){ // 如果剩余时间小于等于0
clearTimeout(timer) // 清除定时器
lastTime = nowTime // 更新上次执行时间
fn()
}else if(!timer){ // 如果定时器为null
timer = setTimeout(()=>{ // 创建新的定时器
lastTime = Number(new Date()) // 更新上一次执行时间
timer = null // 清空定时器变量
fn()
},delay)
}
}
}
FAQ
1、关于textInput禁用拍照输入功能
textinput无法禁止拍摄输入,这是键盘自带功能,但是可以自定义键盘取消拍摄输入:支持开发者定制系统菜单选项,通过editmenuoptions接口,可以把菜单中的拍摄输入屏蔽掉editMenuOptions。
2、关于textInput禁止粘贴。
修改onPaste中的相关实现逻辑,调用event.preventDefault!()可实现禁止粘贴。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。