前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从零开始构建React Native数字键盘功能

从零开始构建React Native数字键盘功能

作者头像
前端小智@大迁世界
发布2024-03-01 13:58:33
1120
发布2024-03-01 13:58:33
举报
文章被收录于专栏:终身学习者终身学习者

现代移动应用程序在入门过程中经常涉及一个步骤,你需要输入发送到你的电子邮件或手机号码的验证码 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应用:

代码语言:javascript
复制
npx create-expo-app my-app

上述命令将创建我们所需的基础React Native项目文件。完成后,启动iOS或Android模拟器上的开发服务器:

代码语言:javascript
复制
//for iOS
npm run ios 

//for Android
npm run android
image.png
image.png

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

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

在这个部分,我们将开始创建三个屏幕: LoginCustomDialpadHome

Login 屏幕将是用户初次加载应用时看到的第一个屏幕。它将有一个按钮,可以将用户引导到 CustomDialpad 屏幕,在那里他们可以输入他们的PIN码。一旦输入正确的PIN码,应用将会将用户引导到 Home 屏幕。

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

代码语言:javascript
复制
npx install @react-navigation/native @react-navigation/native-stack react-native-safe-area-context react-native-screens

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

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

代码语言:javascript
复制
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 组件文件:

代码语言:javascript
复制
//CustomDialPad.js

import {
 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数字键盘,我们首先需要创建一些变量:

代码语言:javascript
复制
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使我们能够控制屏幕导航。我们还设置了组件结构和样式,并导出自定义组件,使其可以在应用的其他部分中使用:

代码语言:javascript
复制
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 文件中工作。首先,我们将导入所有必要的模块和组件:

代码语言:javascript
复制
import {
 StyleSheet,
 Text,
 View,
 FlatList,
 TouchableOpacity,
} from "react-native";
import React from "react";
import { Feather } from "@expo/vector-icons";

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

代码语言:javascript
复制
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

我们在这个文件中的最后一步是定义我们组件的样式:

代码语言:javascript
复制
export default DialpadKeypad;

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

我们看看我们目前拥有的React Native数字键盘:

image.png
image.png

集成并限制点击功能

我们设置在键盘上按下按钮时的功能。我们使用一个初始数据类型为数组的状态来跟踪键盘上每个按钮按下的值。然后,这将作为一个属性传递给 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 文件中的代码:

代码语言:javascript
复制
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 文件:

代码语言:javascript
复制
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 文件:

代码语言:javascript
复制
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 文件:

代码语言:javascript
复制
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 。

代码语言:javascript
复制
 {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 毫秒。

代码语言:javascript
复制
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 样式对象在键盘上选择数字时应用缩放转换:

代码语言:javascript
复制
 const animatedStyle = {
   transform: [
     {
       scale: animatedValue.interpolate({
         inputRange: [index, index + 1],
         outputRange: [1, 1.3],
         extrapolate: "clamp",
       }),
     },
   ],
 };

我们在这里使用了 interpolate 方法将输入值映射到输出值,确保动画的流畅。 inputRangeoutputRange 属性定义了插值的值。

最后, extrapolate 属性定义了输出值的行为。它的 clamp 值表示输出值在定义的范围内被限制。将 animatedStyle 对象添加到 Animated.View 的样式输入中:

代码语言:javascript
复制
  {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

代码语言:javascript
复制
<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登录等情况。你可以在这个仓库中找到我们演示项目的完整源代码。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-03-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 在React Native应用中数字键盘的使用场景
  • 设置开发环境
  • 创建、渲染和设计React Native数字键盘
  • 设置 CustomDialpad.jsx 文件
  • 设置 DialpadKeypad.jsx 文件
  • 集成并限制点击功能
  • 为输入的PIN添加一个 MultiView
  • 附加说明和建议
  • 比较创建自定义数字键盘的方法
  • 总结
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档