首发于公众号 前端混合开发,欢送关注。

古代挪动应用程序在入门过程中常常波及一个步骤,你须要输出发送到你的电子邮件或手机号码的验证码 PIN。有时,你须要应用相似于宰割 OTP 输出字段的货色来输出 PIN。另一种输出验证码 PIN 的形式是应用拨号盘。

"OTP" 指的是 "一次性明码" (One-Time Password)。这是一种平安机制,用于通过短信或电子邮件向用户发送一次性应用的明码或验证码,以验证用户的身份。

在这篇文章中,咱们将展现如何为 React Native 利用创立一个定制的数字键盘。构建一个定制的 React Native 数字键盘能够作为宰割输出或传统 TextInput 元素的优良替代品,以个性化你的挪动利用设计。

你能够查看咱们的React Native我的项目的残缺源代码,并随着咱们一步步设置数字键盘进行跟踪。让咱们开始吧。

在React Native利用中数字键盘的应用场景

在React Native利用中,有许多业余的数字键盘应用场景。

一个常见的例子是一次性明码(OTP)输出验证。例如,假如你在新用户入门过程中,向他们的手机发送了一个OTP。发送OTP后,用户将被疏导到一个屏幕上,应用数字键盘输入并验证它。

另一个应用场景是为你的利用增加一层平安防护,这对于蕴含敏感信息的利用来说十分重要。当你的用户从新登录你的利用时,你能够为他们展现一个数字键盘,他们能够在此输出一个PIN码,你的利用在让他们登录前须要验证这个PIN码。

在咱们的教程中,咱们将创立这第二种用例的一个简略示例。咱们将看到如何在 React Native 中从头开始设置一个数字键盘,以便用户能够创立一个 PIN 并应用该 PIN 登录利用。

设置开发环境

运行以下命令以疾速启动一个Expo利用:

npx create-expo-app my-app

上述命令将创立咱们所需的根底React Native我的项目文件。实现后,启动iOS或Android模拟器上的开发服务器:

//for iOSnpm run ios //for Androidnpm run android

这是你我的项目文件夹中 App.js 文件内代码的输入。

创立、渲染和设计React Native数字键盘

在这个局部,咱们将开始创立三个屏幕: LoginCustomDialpadHome

Login 屏幕将是用户首次加载利用时看到的第一个屏幕。它将有一个按钮,能够将用户疏导到 CustomDialpad 屏幕,在那里他们能够输出他们的PIN码。一旦输出正确的PIN码,利用将会将用户疏导到 Home 屏幕。

咱们开始构建咱们的React Native应用程序,蕴含这三个屏幕。首先,装置咱们须要设置和配置React Native根本导航的以下包:

npx install @react-navigation/native @react-navigation/native-stack react-native-safe-area-context react-native-screens

另外,创立一个名为 screens 的文件夹,并在其中放入三个文件: Login.jsxCustomDialPad.jsxHomeScreen.jsx

接下来,在你的 App.js 文件中,依照上面所示实现根本的导航:

import { StyleSheet } from "react-native";import { NavigationContainer } from "@react-navigation/native";import { createNativeStackNavigator } from "@react-navigation/native-stack";import HomeScreen from "./screens/HomeScreen";import Login from "./screens/Login";import CustomDialPad from "./screens/CustomDialPad";const Stack = createNativeStackNavigator();export default function App() { return (   <NavigationContainer>     <Stack.Navigator       screenOptions={{         headerShown: false,       }}     >       <Stack.Screen name="Login" component={Login} />       <Stack.Screen name="Dialpad" component={CustomDialPad} />       <Stack.Screen name="Home" component={HomeScreen} />     </Stack.Navigator>   </NavigationContainer> );}const styles = StyleSheet.create({ container: {   flex: 1,   backgroundColor: "#fff",   alignItems: "center",   justifyContent: "center", },});

在这里,咱们将整个应用程序包裹在 NavigationContainer 内,并应用 Stack.Navigator 组件来治理屏幕堆栈。当用户导航到一个屏幕时,它会被推到堆栈的顶部。而后,当用户导航到另一个页面时,它会从堆栈顶部弹出屏幕。

在这种状况下,堆栈顶部的初始屏幕将是 Login 屏幕。当用户按下按钮导航到 CustomDialpad 屏幕时, CustomDialpad 屏幕会被推到 Login 屏幕的上方,依此类推:

当初屏幕导航曾经全副设置好了,咱们能够开始设置数字键盘的逻辑和用户界面。

设置 CustomDialpad.jsx 文件

在根目录中创立一个 component 文件夹,并在其中增加一个 DialpadKeypad.jsx 文件。稍后咱们将在此文件中构建咱们的数字键盘界面和性能。

而后,在 CustomDialpad.jsx 文件中导入 DialpadKeypad.jsx 组件文件:

//CustomDialPad.jsimport { SafeAreaView, StyleSheet, Text, View, Dimensions,} from "react-native";import React, { useState } from "react";import { Ionicons } from "@expo/vector-icons";import DialpadKeypad from "../components/DialpadKeypad";const { width, height } = Dimensions.get("window");

咱们还应用了 Dimensions.get 办法来提取用户设施的屏幕 widthheight 。这将帮忙咱们确保咱们的用户界面可能响应不同的屏幕尺寸进行适应。

接下来,为了开始构建咱们的React Native数字键盘,咱们首先须要创立一些变量:

const dialPadContent = [1, 2, 3, 4, 5, 6, 7, 8, 9, "", 0, "X"];const dialPadSize = width * 0.2;const dialPadTextSize = dialPadSize * 0.4;const pinLength = 4;

咱们简要回顾一下每个变量的目标:

  • dialPadContent — 咱们将在数字键盘 UI 上显示的内容。在这种状况下,咱们想要显示一个由十二个值组成的数组,这些值被排列在一个三列四行的网格中。
  • pinLength — 用户应输出的PIN码长度。咱们心愿用户输出一个四位数的PIN码,但这能够依据你的我的项目需要进行调整。
  • dialPadSize — 数字键盘的大小,由手机屏幕的 width 乘以 0.2 得出,占屏幕 width 的20%
  • dialPadTextSize — 显示在数字键盘内的文本大小,由将 dialPadSize 值乘以 0.4 失去 dialPadSize 的40%来确定

CustomDialpad.jsx 文件的其余部分,咱们定义了 CustomDialPad 组件,并应用 useNavigation Hook使咱们可能管制屏幕导航。咱们还设置了组件构造和款式,并导出自定义组件,使其能够在利用的其余局部中应用:

const CustomDialPad = () => { const navigation = useNavigation(); return (   <SafeAreaView style={styles.container}>     <View style={styles.textContainer}>       <TouchableOpacity         onPress={() => navigation.goBack()}         style={{ position: "absolute", top: -5, left: 10 }}       >         <Ionicons name="ios-chevron-back" size={45} color="#5E454B" />       </TouchableOpacity>       <Text style={styles.pinText}>Create PIN</Text>       <Text style={styles.pinSubText}>Enter your secure four-digit code</Text>       <DialpadKeypad         dialPadContent={dialPadContent}         pinLength={pinLength}         dialPadSize={dialPadSize}         dialPadTextSize={dialPadTextSize}       />     </View>   </SafeAreaView> );};export default CustomDialPad;const styles = StyleSheet.create({ container: {   flex: 1,   backgroundColor: "#FAF0E6", }, textContainer: {   justifyContent: "center",   alignItems: "center",   marginTop: 40,   position: "relative", }, pinText: {   fontSize: 30,   fontWeight: "medium",   color: "#5E454B", }, pinSubText: {   fontSize: 18,   fontWeight: "medium",   color: "#5E454B",   marginVertical: 30, },}

此外,咱们应用 TouchableOpacity 组件实现了一个返回按钮,使用户可能通过 navigation.goBack() 办法返回到 Login 页面。

设置 DialpadKeypad.jsx 文件

当初让咱们在 DialpadKeypad.js 文件中工作。首先,咱们将导入所有必要的模块和组件:

import { StyleSheet, Text, View, FlatList, TouchableOpacity,} from "react-native";import React from "react";import { Feather } from "@expo/vector-icons";

接下来,让咱们拿到咱们在 CustomDialPad.js 文件中传递给组件的属性,并用它们来构建键盘的用户界面。而后,咱们将应用 Flatlist 来渲染咱们之前定义的 dialPadContent 数组。

const DialpadKeypad = ({ dialPadContent, pinLength, navigation, dialPadSize, dialPadTextSize,}) => { return (   <FlatList     data={dialPadContent}     numColumns={3} // set number of columns     keyExtractor={(_, index) => index.toString()}     renderItem={({ item }) => {       return (         <TouchableOpacity           disabled={item === ""} // make the empty space on the dialpad content unclickable          >           <View             style={[               {                 backgroundColor: item === "" ? "transparent" : "#fff",                 width: dialPadSize,                 height: dialPadSize,               },               styles.dialPadContainer,             ]}           >             {item === "X" ? (               <Feather name="delete" size={24} color="#3F1D38" />             ) : (               <Text                 style={[{ fontSize: dialPadTextSize }, styles.dialPadText]}               >                 {item}               </Text>             )}           </View>         </TouchableOpacity>       );     }}   /> );};

咱们将 numColumns 属性设置为 3 ,以便在三列中渲染咱们的 dialPadContent 数组。数组中的空白 "" 值使咱们能够使渲染的三列四行数字键盘在视觉上更加均衡。

在数字键盘上,咱们使空白按钮不能被按压,并移除了它的背景色。咱们还为数组中对应 X 值的按钮渲染了一个删除图标。对于数字键盘上的其余按钮,咱们渲染了数组中的数字。

咱们还将 View 组件包裹在 TouchableOpacity 组件内,以渲染 dialpadContent

咱们在这个文件中的最初一步是定义咱们组件的款式:

export default DialpadKeypad;const styles = StyleSheet.create({ dialPadContainer: {   justifyContent: "center",   alignItems: "center",   margin: 10,   borderRadius: 50,   borderColor: "transparent", }, dialPadText: {   color: "#3F1D38", },});

咱们看看咱们目前领有的React Native数字键盘:

集成并限度点击性能

咱们设置在键盘上按下按钮时的性能。咱们应用一个初始数据类型为数组的状态来跟踪键盘上每个按钮按下的值。而后,这将作为一个属性传递给 DialpadKeypad 组件。

DialpadKeypad 文件中,咱们将采纳 codesetCode 属性,并应用它们来实现所需的性能。当点击 Keypad 内容时,咱们将首先调用 onPress 属性进行查看:

  • 如果按下的按钮的值为 X 。如果是这样,它应该删除数组中的最初一个我的项目——换句话说,删除最初抉择的PIN值。
  • 如果按下的按钮的值是除了 X 之外的任何值。如果是,它应该应用 setCode 属性将选中的我的项目增加到代码数组中。
  • 如果代码数组的长度等于 pinLength - 1 。如果是这样,应该将用户导航到 Home 屏幕。

咱们应用 pinLength - 1code 属性的长度进行比照,是因为所需的 pinLength 被指定为 4 。

如果 code 状态数组中有四个我的项目,长度将为 3 ,因为数组中的索引值从 0 开始。因而,一旦将四位数的PIN输出到 code 数组中,咱们就应用 pinLength -1 来导航到 Home 屏幕。

为了实现所有这些,咱们须要像这样更新 CustomDialPad.js 文件中的代码:

const CustomDialPad = () => { const navigation = useNavigation(); const [code, setCode] = useState([]);// rest of the code   <DialpadKeypad         dialPadContent={dialPadContent}         pinLength={pinLength}         setCode={setCode}         code={code}         dialPadSize={dialPadSize}         dialPadTextSize={dialPadTextSize}       />

同样地,像这样更新 DialpadKeypad.js 文件:

const DialpadKeypad = ({ dialPadContent, pinLength, code, setCode, navigation, dialPadSize, dialPadTextSize,}) => {// rest of the code    <TouchableOpacity           disabled={item === ""} // make the empty space on the dialpad content unclickable           onPress={() => {             if (item === "X") {               setCode((prev) => prev.slice(0, -1));             } else {               if (code.length === pinLength - 1) {                 navigation.navigate("Home");               }               setCode((prev) => [...prev, item]);             }           }}         >

为输出的PIN增加一个 MultiView

在这一部分,咱们将增加一个 MultiView。在这个实例中,这是一个视图,容许咱们查看所选输出 — 换句话说,就是输出的 PIN 码。

首先,在组件文件夹中创立一个 DialpadPin.js 文件,并在 CustomDialPad 组件中渲染它。而后,咱们将 pinLengthpinSizecodedialPadContent 属性传递给 DialpadPin.js 文件。

DialpadPin.js 文件中,咱们将依据咱们之前设定的 4 的PIN长度渲染一个 View 。咱们心愿在 CustomDialpad 屏幕上将其作为四个均匀分布的圆形排列在输出PIN的提醒和数字键盘之间显示。

在渲染的视图外部,咱们还将渲染 PIN 值,这将让咱们晓得是否已抉择了一个值。如果从键盘上抉择了一个值,咱们将在 MultiView 中显示它,这样用户就晓得他们以后在输出中抉择了多少位数字。

要实现所有这些,请依照以下形式更新 CustomDialPad.js 文件:

const dialPadContent = [1, 2, 3, 4, 5, 6, 7, 8, 9, "", 0, "X"];const dialPadSize = width * 0.2;const dialPadTextSize = dialPadSize * 0.4;const pinLength = 4;const pinContainerSize = width / 2;const pinSize = pinContainerSize / pinLength;const CustomDialPad = () => { const fontsLoaded = useCustomFonts(); const navigation = useNavigation(); const [code, setCode] = useState([])// rest of the code        <DialpadPin         pinLength={pinLength}         pinSize={pinSize}         code={code}         dialPadContent={dialPadContent}       />

而后,也更新 DialpadPin.js 文件:

import { StyleSheet, Text, View } from "react-native";import React from "react";const DialpadPin = ({ pinLength, pinSize, code, dialPadContent }) => { return (   <View style={styles.dialPadPinContainer}>     {Array(pinLength)       .fill()       .map((_, index) => {         const item = dialPadContent[index];         const isSelected =           typeof item === "number" && code[index] !== undefined;         return (           <View             key={index}             style={{               width: pinSize,               height: pinSize,               borderRadius: pinSize / 2,               overflow: "hidden",               margin: 5,             }}           >             <View               style={[                 {                   borderRadius: pinSize / 2,                   borderColor: !isSelected ? "lightgrey" : "#3F1D38",                 },                 styles.pinContentContainer,               ]}             >               {isSelected && (                 <View                   style={[                     {                       width: pinSize * 0.5,                       height: pinSize * 0.5,                       borderRadius: pinSize * 0.35,                     },                     styles.pinContent,                   ]}                 />               )}             </View>           </View>         );       })}   </View> );};export default DialpadPin;const styles = StyleSheet.create({ dialPadPinContainer: {   flexDirection: "row",   marginBottom: 30,   alignItems: "flex-end", }, pinContentContainer: {   flex: 1,   backgroundColor: "#fff",   borderWidth: 1,   justifyContent: "center",   alignItems: "center", }, pinContent: {   backgroundColor: "#5E454B", },});

当初,让咱们看看咱们有什么:

咱们能够进一步去动画化从数字键盘中选中的点状引脚。在 DialpadPin.jsx 文件中,导入 Animated 库,这是React Native提供的开箱即用的。而后,用 Animated.View 包裹显示点状抉择的 View 。

 {isSelected && (    <Animated.View     style={[         {           width: pinSize * 0.5,           height: pinSize * 0.5,           borderRadius: pinSize * 0.35,          },     styles.pinContent,    ]}   />  )}

当初咱们将创立一个 useEffect 钩子,每当代码的值发生变化时都会触发。每当用户在键盘上输出一个数字,都会应用 Animation.timing 办法触发动画。 animatedValue 将从其以后值动画过渡到 code.length 值,过程继续 300 毫秒。

const DialpadPin = ({ pinLength, pinSize, code, dialPadContent }) => { const animatedValue = useRef(new Animated.Value(0)).current; useEffect(() => {   Animated.timing(animatedValue, {     toValue: code.length,     duration: 300,     useNativeDriver: true,   }).start(); }, [code]);

接下来,咱们将应用 animatedStyle 款式对象在键盘上抉择数字时利用缩放转换:

 const animatedStyle = {   transform: [     {       scale: animatedValue.interpolate({         inputRange: [index, index + 1],         outputRange: [1, 1.3],         extrapolate: "clamp",       }),     },   ], };

咱们在这里应用了 interpolate 办法将输出值映射到输入值,确保动画的晦涩。 inputRangeoutputRange 属性定义了插值的值。

最初, extrapolate 属性定义了输入值的行为。它的 clamp 值示意输入值在定义的范畴内被限度。将 animatedStyle 对象增加到 Animated.View 的款式输出中:

  {isSelected && (   <Animated.View     style={[       {         width: pinSize * 0.5,         height: pinSize * 0.5,         borderRadius: pinSize * 0.35,       },       styles.pinContent,       animatedStyle,     ]}   />  )}

咱们增加动画后的最终后果应如下所示:

如你所见,黑白的点首先以略微小一些的模式呈现在 MultiView 气泡中,而后扩充以更齐全地填充气泡。这使咱们的数字键盘性能在不过分扩散注意力的状况下,以一种奥妙的形式变得更具视觉吸引力。

附加阐明和倡议

为了在实在的React Native利用中改良这个数字键盘的实现,咱们须要设置一个后端服务来与咱们的前端实现进行通信。让咱们回顾一下这对咱们每个用例会波及到什么。

咱们探讨的第一个用例是在新用户注册过程中,应用数字键盘验证发送到用户手机或电子邮件的一次性明码。因而,当有新用户注册你的利用时,你须要:

  • 验证他们用来注册的电子邮件
  • 从你的后端服务发送一次性明码
  • 领导他们到一个蕴含数字键盘的屏幕,他们能够在那里输出你发送到他们邮箱的一次性明码

当初,用户须要应用数字键盘输入他们收到的OTP。现实状况下,当他们输出残缺的OTP后,你应该可能向后端的 verify 端点发送申请,以验证你发送给该用户的OTP是否与他们在前端输出的匹配

  • 如果匹配,将他们导航至 Home 屏幕
  • 如果不匹配,显示一个定制的错误信息,通知他们输出的PIN码谬误,他们应该输出发送到他们邮箱的正确PIN码

在咱们以后的我的项目中,咱们没有验证PIN,因为咱们没有设置后端服务。然而,如果你在一个实在的我的项目中设置这个, verify 端点应该在 DialpadKeypad.js 文件中被调用,咱们在那里查看 code.lengthpinLength

<TouchableOpacity  disabled={item === ""} // 使拨号盘内容上的空白区域不可点击  onPress={() => {  if (item === "X") {   setCode((prev) => prev.slice(0, -1));   } else {    if (code.length === pinLength - 1) {     // 一旦用户输出了所需的 PIN 长度,在这里调用端点     navigation.navigate("Home");    }    setCode((prev) => [...prev, item]);    }    }}   >

咱们探讨的第二个用例是应用数字键盘进行登录平安。就像第一个用例一样,你能够在你的应用程序中自定义数字键盘,显示在你的登录页面上。

用户在注册时能够输出一个PIN码。而后,当用户从新输出他们的PIN码以从新登录利用时,你能够让你的后端端点验证在注册期间创立的明码是否与正在输出的明码匹配。

如果你的后端端点验证了匹配,你能够容许用户登录。如果没有,你能够显示一个定制的正告音讯 - 例如, Pin does not match

这个用例确保用户在没有必要的安全检查的状况下,不会仅仅进入应用程序。

比拟创立自定义数字键盘的办法

React Native反对几种不同的创立数字键盘的办法。例如,咱们能够应用 TextInput 组件,并将键盘类型作为 numeric 来设置咱们的数字键盘。然而,这种办法存在一些已知的问题:

  • 点击组件内部时无奈打消:这个问题意味着即便你在 TextInput 内部点击,数字键盘依然放弃关上状态。解决这个问题的可能办法是应用 TouchableWithoutFeedback API组件,在你点击它内部时打消 TextInput 键盘。
  • 按返回键未能打消:这个问题意味着当你按下返回键时,数字键盘不会主动隐没

也有一些现有的开源库提供数字键盘性能,包含 React Native NumpadReact Native Numeric Pad。然而,这些库在性能和可定制性方面有些限度。

在许多状况下,你的React Native利用可能有独特的设计和特定的需要,对于数字键盘性能应该如何构建和施行。构建自定义性能意味着你不会受到库的能力的限度。

此外,在你的React Native应用程序中装置过多的包会使其变得臃肿。自行构建性能并缩小装置的包能够帮忙减小应用程序的大小。

最初,库可能不会继续沉闷地保护,甚至可能齐全被遗弃,这可能会导致你的利用解体。如果你抉择应用第三方库,始终尝试应用稳固且保护良好的选项。

你抉择的办法取决于你的我的项目需要。例如,应用库能够帮忙你节俭大量的开发工夫。然而,如果你须要特定的性能或定制,那么投入工夫来构建你本人的可能会更好。

总结

在这篇文章中,咱们学习了如何在React Native中创立自定义数字键盘。咱们还将咱们的办法与其余选项进行了比拟,比方内置的 TextInput 组件和第三方开源库,以更好地了解何时以及为什么要从头开始构建这个性能。

自定义数字键盘是一款杰出的挪动利用性能,实用于像应用一次性明码验证用户或让他们应用PIN登录等状况。你能够在这个仓库中找到咱们演示我的项目的残缺源代码。

交换

首发于公众号 大迁世界,欢送关注。 每周一篇实用的前端文章 ️ 分享值得关注的开发工具 ❓ 有疑难?我来答复

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。