目录

一、核心知识点:数字验证码输入 完整技术体系

1、核心内置 API / 组件 / Hook 介绍

2、鸿蒙端验证码输入 核心实现原则

3、鸿蒙官方验证码输入 设计 & 交互规范

二、实战:鸿蒙手机端 数字验证码输入组件

三、OpenHarmony6.0 专属避坑指南

四、扩展用法:验证码输入 高频进阶功能

扩展 1:结合倒计时 “获取验证码” 按钮

扩展 2:自动填充系统验证码

扩展 3:验证码错误提示动画

扩展 4:深色模式适配


一、核心知识点:数字验证码输入 完整技术体系

1、核心内置 API / 组件 / Hook 介绍

本次实现鸿蒙端数字验证码输入,全部基于 RN 原生能力(无第三方库),完美适配鸿蒙手机端的输入交互,核心能力如下:

核心 API / 组件 / Hook 作用说明 鸿蒙端特性
TextInput 验证码输入框(单字符分箱) 支持鸿蒙端numeric键盘、光标自定义,无输入延迟
useState 管理每个输入框的内容、聚焦状态 响应式更新,鸿蒙端无重渲染卡顿
useRef 管理输入框引用,实现自动聚焦切换 鸿蒙端ref.current.focus()需延迟执行(避坑点)
Keyboard 控制键盘收起 / 弹出 鸿蒙端Keyboard.dismiss()原生支持,无延迟
onKeyPress 监听删除键事件,实现删除后聚焦上一输入框 鸿蒙端需监听backspace键值,适配原生键盘

2、鸿蒙端验证码输入 核心实现原则

遵循「分箱输入、自动流转、交互贴合、鸿蒙适配」四大原则,是企业级鸿蒙 RN 项目的标准方案:

  1. 分箱输入:将验证码拆分为 N 个独立输入框(通常 4-6 个),每个输入框仅允许输入 1 位数字;
  2. 自动流转:输入完成当前框后,自动聚焦下一个输入框;删除当前框内容后,自动聚焦上一个输入框;
  3. 输入限制:强制限制数字输入、禁止特殊字符,鸿蒙端键盘默认唤起数字键盘;
  4. 鸿蒙交互:贴合鸿蒙端输入习惯(如光标样式、输入框高亮、键盘收起逻辑)。

3、鸿蒙官方验证码输入 设计 & 交互规范

鸿蒙系统对验证码输入组件有明确的视觉 / 交互规范,遵循后体验与原生应用一致:

  • 样式规范:输入框宽度48px、高度56px,圆角8px,未输入时边框为#E5E5E5,聚焦时为鸿蒙主题蓝#007DFF
  • 交互规范:输入框默认自动聚焦第一个,输入后自动跳转到下一个,最后一个输入完成后自动收起键盘;
  • 适配规范:鸿蒙手机端输入框间距12px,平板端间距16px,光标颜色与输入框聚焦边框色一致。

二、实战:鸿蒙手机端 数字验证码输入组件

import React, { useState, useRef, useCallback } from 'react';
import {
  View, TextInput, StyleSheet, SafeAreaView,
  Keyboard, TextInputProps, Dimensions, Text, StyleProp, TextStyle
} from 'react-native';

const { width } = Dimensions.get('window');
const CODE_BOX_COUNT = 4;
const CODE_BOX_WIDTH = 48;
const CODE_BOX_SPACING = 12;

type CodeInputRef = React.RefObject<TextInput>;
type CodeValue = string[];

const HarmonyCodeInput: React.FC = () => {
  const [codeValue, setCodeValue] = useState<CodeValue>(Array(CODE_BOX_COUNT).fill(''));
  const inputRefs = useRef<CodeInputRef[]>(
    Array.from({ length: CODE_BOX_COUNT }).map(() => useRef<TextInput>(null))
  );

  const handleInputChange = useCallback((index: number, value: string) => {
    const numericValue = value.replace(/[^0-9]/g, '');
    if (numericValue.length > 1) return;

    const newCodeValue = [...codeValue];
    newCodeValue[index] = numericValue;
    setCodeValue(newCodeValue);

    if (numericValue && index < CODE_BOX_COUNT - 1) {
      setTimeout(() => {
        inputRefs.current[index + 1]?.current?.focus();
      }, 0);
    }

    if (index === CODE_BOX_COUNT - 1 && numericValue) {
      Keyboard.dismiss();
    }
  }, [codeValue]);

  const handleKeyPress = useCallback((index: number, e: { nativeEvent: { key: string } }) => {
    if (e.nativeEvent.key === 'Backspace' && !codeValue[index]) {
      if (index > 0) {
        setTimeout(() => {
          inputRefs.current[index - 1]?.current?.focus();
        }, 0);
      }
    }
  }, [codeValue]);

  const renderCodeBox = (index: number) => {
    const inputProps: TextInputProps = {
      style: [styles.codeInput, !!codeValue[index] && styles.codeInputFilled] as StyleProp<TextStyle>,
      value: codeValue[index],
      onChangeText: (value: string) => handleInputChange(index, value),
      onKeyPress: (e: { nativeEvent: { key: string } }) => handleKeyPress(index, e),
      keyboardType: 'numeric',
      autoCapitalize: 'none' as 'none',
      autoCorrect: false,
      blurOnSubmit: false,
      maxLength: 1,
      textAlign: 'center' as 'center',
      selectionColor: '#007DFF',
      autoFocus: index === 0,
    };

    return (
      <TextInput
        {...inputProps}
        ref={inputRefs.current[index]} 
        key={index}
      />
    );
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.titleWrap}>
        <Text style={styles.title}>请输入4位数字验证码</Text>
      </View>
      
      <View style={styles.codeBoxWrap}>
        {Array.from({ length: CODE_BOX_COUNT }).map((_, index) => renderCodeBox(index))}
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5F5F5',
    alignItems: 'center',
    paddingTop: 60,
  },
  titleWrap: {
    marginBottom: 40,
  },
  title: {
    fontSize: 18,
    color: '#333333',
    fontWeight: '500',
  },
  codeBoxWrap: {
    flexDirection: 'row',
    justifyContent: 'center',
    width: CODE_BOX_COUNT * (CODE_BOX_WIDTH + CODE_BOX_SPACING) - CODE_BOX_SPACING,
  },
  codeInput: {
    width: CODE_BOX_WIDTH,
    height: 56,
    borderRadius: 8,
    borderWidth: 1,
    borderColor: '#E5E5E5',
    marginRight: CODE_BOX_SPACING,
    fontSize: 22,
    fontWeight: '600',
    color: '#333333',
  },
  codeInputFilled: {
    borderColor: '#007DFF',
  },
});

export default HarmonyCodeInput;

三、OpenHarmony6.0 专属避坑指南

以下是鸿蒙 RN 开发中实现验证码输入的高频踩坑点,全部为鸿蒙端专属问题 + 解决方案:

问题现象 问题原因 鸿蒙端最优解决方案
输入框无法自动聚焦下一个 鸿蒙端focus()需延迟执行,直接调用可能失效 handleInputChange中用setTimeout(() => inputRef.current.focus(), 0)
输入框可以输入非数字字符 未设置keyboardType: 'numeric',或未过滤输入内容 同时设置keyboardTypeonChangeText过滤非数字(value.replace(/[^0-9]/g, '')
输入框边框样式异常(圆角不生效) 鸿蒙端TextInputborderRadius需配合overflow: 'hidden' View包裹输入框,在 View 上设置边框 + 圆角
删除键按下后无响应(不跳转到上一个) 未监听Backspace键,或当前输入框值判断错误 onKeyPress中监听e.nativeEvent.key === 'Backspace',并判断当前输入框是否为空
输入完成后键盘不自动收起 未调用Keyboard.dismiss(),或调用时机错误 在最后一个输入框的handleInputChange中,输入完成后执行Keyboard.dismiss()
输入框光标颜色与鸿蒙主题不符 未设置selectionColor,默认颜色与主题冲突 设置selectionColor: '#007DFF'(鸿蒙官方主题蓝)
鸿蒙平板端输入框间距过小 / 过大 硬编码间距,未适配平板尺寸 Dimensions动态计算间距:平板端间距设为 16px,手机端为 12px
TS 报错inputRef.current可能为 null 未对 ref 做非空判断,TS 类型校验严格 调用focus()前加可选链:inputRefs.current[index + 1]?.current?.focus()

四、扩展用法:验证码输入 高频进阶功能

基于本次基础代码,可快速扩展鸿蒙端项目常用的验证码功能:

扩展 1:结合倒计时 “获取验证码” 按钮

在输入框上方添加 “获取验证码” 按钮,用useState管理倒计时(如 60 秒),点击后禁用按钮并开始倒计时,倒计时结束后恢复可用,同时适配鸿蒙端按钮样式规范。

扩展 2:自动填充系统验证码

结合鸿蒙端ClipboardAPI(import { Clipboard } from 'react-native'),在组件挂载后读取剪贴板内容,若为 4 位数字则自动填充到输入框中,提升用户体验。

扩展 3:验证码错误提示动画

输入错误验证码后,给输入框添加 “抖动” 动画(用Animated实现),同时将输入框边框改为红色,提示用户输入错误,符合鸿蒙端交互反馈规范。

扩展 4:深色模式适配

结合之前的useColorScheme,抽离 “浅色 / 深色” 两套验证码样式(输入框背景、边框色、文字色),动态绑定到组件中,适配鸿蒙系统的深色模式。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐