├── .gitattributes
├── .gitignore
├── README.md
├── README_CN.md
├── index.js
├── package-lock.json
├── package.json
└── src
├── component
├── ParcelKeyboardView.js
├── keyboardMethods.js
├── securityKeyboardBase.js
└── securityKeyboardInput.js
├── resource
├── images
│ ├── back.png
│ ├── icon-delete.png
│ ├── icon-down.png
│ ├── space.png
│ ├── text.png
│ ├── transform.png
│ └── transform2.png
└── json
│ └── keyboard.js
└── style
├── securityKeyboard.js
└── securityKeyboardInput.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # OSX
3 | #
4 | .DS_Store
5 |
6 | # node.js
7 | #
8 | node_modules/
9 | npm-debug.log
10 | yarn-error.log
11 |
12 |
13 | # Xcode
14 | #
15 | build/
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 | xcuserdata
25 | *.xccheckout
26 | *.moved-aside
27 | DerivedData
28 | *.hmap
29 | *.ipa
30 | *.xcuserstate
31 | project.xcworkspace
32 |
33 |
34 | # Android/IntelliJ
35 | #
36 | build/
37 | .idea
38 | .gradle
39 | local.properties
40 | *.iml
41 |
42 | # BUCK
43 | buck-out/
44 | \.buckd/
45 | *.keystore
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # react-native-supervons-custom-keyboard
3 | [](https://www.npmjs.com/package/react-native-supervons-custom-keyboard)
4 | [](https://www.npmjs.com/package/react-native-supervons-custom-keyboard)
5 |
6 | English [简体中文](/README_CN.md "中文介绍")
7 |
8 | # github(welcome star━(*`∀´*)ノ亻!)
9 | https://github.com/supervons/react-native-supervons-custom-keyboard
10 |
11 | ## Summary
12 | ```
13 | Developed based on react-native-security-keyboard,add:
14 | 1. Uppercase and lowercase letters
15 | 2. Character
16 | 3. Key response
17 | 4. Switch keyboard type
18 | 5. Randomly arrange the keyboard
19 |
20 | Thanks yanzhandong source sharing
21 | ```
22 | ## Preview gif
23 |
24 |
25 |
26 | ## Preview
27 | 
28 |
29 | ## DEMO
30 | https://github.com/supervons/ExploreRN/blob/master/src/screens/login/index.js
31 |
32 | ## Getting started
33 |
34 | `$ npm install react-native-supervons-custom-keyboard --save`
35 |
36 |
37 | ## Usage
38 | ```js
39 | import React, { Component } from 'react';
40 | import { View, Button } from 'react-native';
41 | import { SecurityKeyboardInput } from 'react-native-supervons-custom-keyboard';
42 | ```
43 | ```jsx
44 | export default class MainPage extends Component {
45 | constructor(props) {
46 | super(props);
47 | this.state = {
48 | inputValue: ''
49 | };
50 | }
51 |
52 | render() {
53 | return (
54 |
55 |
56 | this.setState({ inputValue: text })}
67 | />
68 |
70 |
71 | );
72 | }
73 | }
74 | ```
75 |
76 | ### Multiple password Input
77 | Please add property: keyName, it's unique .
78 | ```jsx
79 |
81 |
83 | ```
84 |
85 | ### Props
86 |
87 | | **Prop** | **Type** | **Description** |
88 | |----------|----------|-----------------|
89 | | `keyName` | `String` | Keep the keyboard unique, required. |
90 | | `disabled` | `Boolean` | prohibit input, The default is false. |
91 | | `random` | `Boolean` | random keyboard layout, The default is false. |
92 | | `caretHidden` | `Boolean` | hide cursor, The default is false. |
93 | | `secureTextEntry` | `String` |password modal, The default is false.. |
94 | | `placeholderTextColor` | `String` | The color of the text displayed by the placeholder string. |
95 | | `style` | `Object` | custom TextInput external style Style, does not support font Style. |
96 | | `valueStyle` | `Object` | Text content style.|
97 | | `keyboardHeader` | `element` | Customizing the top of the keyboard.|
98 | | `regs` | `Func(value)` | value check, need to return the check after the value out. |
99 | | `onChangeText` | `Func(value)` | Value modified callback. |
100 | | `onFocus` | `Func` | The callback function of the focus. |
101 | | `onBlur` | `Func` | A callback function that loses focus |
102 |
103 | ### Methods
104 |
105 | | **Method** | **Parameter** | **Description** |
106 | |------------|---------------|-----------------|
107 | | `clear` | none | Clear all the content |
108 | | `isFocused` | none |The return value indicates whether the current input box has got the focus. |
109 | | `blur` | none | Lose focus. |
110 | | `focus` | none | Get the focus. |
111 |
112 |
113 | # TODO-LIST
114 |
115 | - [ ] Support typescript
116 | - [ ] Change to hooks
117 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 |
2 | # react-native-supervons-custom-keyboard
3 | [](https://www.npmjs.com/package/react-native-supervons-custom-keyboard)
4 | [](https://www.npmjs.com/package/react-native-supervons-custom-keyboard)
5 |
6 | [English](/README.md "english readme") 简体中文
7 |
8 | # github 地址(欢迎关注━(*`∀´*)ノ亻!)
9 | https://github.com/supervons/react-native-supervons-custom-keyboard
10 |
11 | ## 综述
12 | ```
13 | 基于 react-native-security-keyboard 开发,增加:
14 | 1. 大小写字母
15 | 2. 字符
16 | 3. 按键响应
17 | 4. 切换键盘类型
18 | 5. 随机排列键盘
19 |
20 | 感谢 yanzhandong 的开源分享
21 | ```
22 | ## 预览 gif
23 |
24 |
25 |
26 | ## 预览
27 | 
28 |
29 | ## 实战示例
30 | https://github.com/supervons/ExploreRN/blob/master/src/screens/login/index.js
31 |
32 | ## 开始
33 |
34 | `$ npm install react-native-supervons-custom-keyboard --save`
35 |
36 |
37 | ## 示例
38 | ```js
39 | import React, { Component } from 'react';
40 | import { View, Button } from 'react-native';
41 | import { SecurityKeyboardInput } from 'react-native-supervons-custom-keyboard';
42 | ```
43 | ```jsx
44 | export default class MainPage extends Component {
45 | constructor(props) {
46 | super(props);
47 | this.state = {
48 | inputValue: ''
49 | };
50 | }
51 |
52 | render() {
53 | return (
54 |
55 |
56 | this.setState({ inputValue: text })}
67 | />
68 |
70 |
71 | );
72 | }
73 | }
74 | ```
75 |
76 | ### 同页面展示多个密码框
77 | 请添加 keyName 属性并保证唯一
78 | ```jsx
79 |
81 |
83 | ```
84 |
85 | ### 属性
86 |
87 | | **属性** | **类型** | **简述** |
88 | |----------|----------|-----------------|
89 | | `keyName` | `String` | 保持键盘唯一标识,必填属性 |
90 | | `disabled` | `Boolean` | 禁止输入,默认false |
91 | | `random` | `Boolean` | 键盘字母、数字随机布局,默认false |
92 | | `caretHidden` | `Boolean` | 是否隐藏光标,默认false|
93 | | `secureTextEntry` | `Boolean` | 密码模式,默认false |
94 | | `placeholderTextColor` | `String` | 占位符字符串显示的文本颜色 |
95 | | `style` | `Object` | 自定义TextInput外部样式Style,不支持字体样式 |
96 | | `valueStyle` | `Object` | 字体样式|
97 | | `keyboardHeader` | `element` | 头部元素|
98 | | `regs` | `Func(value)` | 值校验 |
99 | | `onChangeText` | `Func(value)` | 输入值改变回调方法. |
100 | | `onFocus` | `Func` | 获得焦点回调方法. |
101 | | `onBlur` | `Func` | 失去焦点回调方法 |
102 |
103 | ### 方法
104 |
105 | | **方法名** | **参数** | **简述** |
106 | |------------|---------------|-----------------|
107 | | `clear` | none | 清除文本内容 |
108 | | `isFocused` | none | 判断当前输入框是否获得焦点 |
109 | | `blur` | none | 失去焦点. |
110 | | `focus` | none | 获得焦点. |
111 |
112 | # 待办
113 |
114 | - [ ] 支持 Typescript
115 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import SecurityKeyboardInput from "./src/component/keyboardMethods"
2 | import ParcelKeyboardView from "./src/component/ParcelKeyboardView"
3 |
4 | export { SecurityKeyboardInput, ParcelKeyboardView }
5 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-supervons-custom-keyboard",
3 | "version": "1.4.7",
4 | "lockfileVersion": 1
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-supervons-custom-keyboard",
3 | "version": "1.4.7",
4 | "description": "A JS keyboard with uppercase and lowercase letters, numbers and some symbols.\n",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [
10 | "react-native",
11 | "react-native-keyboard",
12 | "react-keyboard",
13 | "react-native-component",
14 | "RNkeyboard",
15 | "keyboard"
16 | ],
17 | "author": "supervons",
18 | "license": "",
19 | "peerDependencies": {
20 | "react-native": "^0.41.2",
21 | "deprecated-react-native-prop-types": "^4.1.0",
22 | "react-native-windows": "0.41.0-rc.1"
23 | },
24 | "dependencies": {
25 | "create-react-class": "^15.5.4"
26 | },
27 | "prettier": {
28 | "singleQuote": false,
29 | "semi": false,
30 | "tabWidth": 2,
31 | "useTabs": false,
32 | "trailingComma": "none",
33 | "bracketSpacing": true,
34 | "arrowParens": "avoid",
35 | "jsxBracketSameLine": false,
36 | "proseWrap": "preserve",
37 | "printWidth": 80
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/component/ParcelKeyboardView.js:
--------------------------------------------------------------------------------
1 | const createReactClass = require("create-react-class")
2 | import {
3 | Keyboard,
4 | LayoutAnimation,
5 | Platform,
6 | View,
7 | DeviceEventEmitter
8 | } from "react-native"
9 | import React from "react"
10 | import ViewPropTypes from "deprecated-react-native-prop-types"
11 | const PropTypes = require("prop-types")
12 | /* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
13 | * found when Flow v0.54 was deployed. To see the error delete this comment and
14 | * run Flow. */
15 | const TimerMixin = require("react-timer-mixin")
16 |
17 | import type EmitterSubscription from "EmitterSubscription"
18 | import type { ViewLayout, ViewLayoutEvent } from "ViewPropTypes"
19 |
20 | type ScreenRect = {
21 | screenX: number,
22 | screenY: number,
23 | width: number,
24 | height: number
25 | }
26 | type KeyboardChangeEvent = {
27 | startCoordinates?: ScreenRect,
28 | endCoordinates: ScreenRect,
29 | duration?: number,
30 | easing?: string
31 | }
32 |
33 | const viewRef = "VIEW"
34 |
35 | /**
36 | * This is a component to solve the common problem of views that need to move out of the way of the virtual keyboard.
37 | * It can automatically adjust either its height, position or bottom padding based on the position of the keyboard.
38 | */
39 | const ParcelKeyboardView = createReactClass({
40 | displayName: "KeyboardAvoidingView",
41 | mixins: [TimerMixin],
42 |
43 | propTypes: {
44 | ...ViewPropTypes,
45 | /**
46 | * Specify how the `KeyboardAvoidingView` will react to the presence of
47 | * the keyboard. It can adjust the height, position or bottom padding of the view
48 | */
49 | behavior: PropTypes.oneOf(["height", "position", "padding"]),
50 |
51 | /**
52 | * The style of the content container(View) when behavior is 'position'.
53 | */
54 | contentContainerStyle: ViewPropTypes.style,
55 |
56 | /**
57 | * This is the distance between the top of the user screen and the react native view,
58 | * may be non-zero in some use cases. The default value is 0.
59 | */
60 | keyboardVerticalOffset: PropTypes.number.isRequired,
61 | /**
62 | * This is to allow us to manually control which KAV shuld take effect when
63 | * having more than one KAV at the same screen
64 | */
65 | enabled: PropTypes.bool.isRequired
66 | },
67 |
68 | getDefaultProps() {
69 | return {
70 | enabled: true,
71 | keyboardVerticalOffset: 0
72 | }
73 | },
74 |
75 | getInitialState() {
76 | return {
77 | bottom: 0
78 | }
79 | },
80 |
81 | subscriptions: ([]: Array),
82 | frame: (null: ?ViewLayout),
83 |
84 | _relativeKeyboardHeight(keyboardFrame: ScreenRect): number {
85 | const frame = this.frame
86 | if (!frame || !keyboardFrame) {
87 | return 0
88 | }
89 |
90 | const keyboardY = keyboardFrame.screenY - this.props.keyboardVerticalOffset
91 |
92 | // Calculate the displacement needed for the view such that it
93 | // no longer overlaps with the keyboard
94 | return Math.max(frame.y + frame.height - keyboardY, 0)
95 | },
96 |
97 | _onKeyboardChange(event: ?KeyboardChangeEvent) {
98 | if (!event) {
99 | this.setState({ bottom: 0 })
100 | return
101 | }
102 |
103 | const { duration, easing, endCoordinates, isLoadKeyBoard } = event
104 | const height = this._relativeKeyboardHeight(endCoordinates)
105 | this.isLoadKeyBoard = isLoadKeyBoard
106 |
107 | if (this.state.bottom === height) {
108 | return
109 | }
110 |
111 | if (duration && easing) {
112 | LayoutAnimation.configureNext({
113 | duration: duration,
114 | update: {
115 | duration: duration,
116 | type: LayoutAnimation.Types[easing] || "keyboard"
117 | }
118 | })
119 | }
120 | this.setState({ bottom: height })
121 | },
122 |
123 | _onLayout(event: ViewLayoutEvent) {
124 | this.frame = event.nativeEvent.layout
125 | },
126 |
127 | UNSAFE_componentWillUpdate(
128 | nextProps: Object,
129 | nextState: Object,
130 | nextContext?: Object
131 | ): void {
132 | if (
133 | nextState.bottom === this.state.bottom &&
134 | this.props.behavior === "height" &&
135 | nextProps.behavior === "height"
136 | ) {
137 | // If the component rerenders without an internal state change, e.g.
138 | // triggered by parent component re-rendering, no need for bottom to change.
139 | nextState.bottom = 0
140 | }
141 | },
142 |
143 | UNSAFE_componentWillMount() {
144 | if (Platform.OS === "ios") {
145 | this.subscriptions = [
146 | Keyboard.addListener("keyboardWillChangeFrame", this._onKeyboardChange)
147 | ]
148 | } else {
149 | this.subscriptions = [
150 | Keyboard.addListener("keyboardDidHide", this._onKeyboardChange),
151 | Keyboard.addListener("keyboardDidShow", this._onKeyboardChange)
152 | ]
153 | }
154 | this.subscriptions.push(
155 | DeviceEventEmitter.addListener("_keyboardDidHide", this._onKeyboardChange)
156 | )
157 | this.subscriptions.push(
158 | DeviceEventEmitter.addListener("_keyboardDidShow", this._onKeyboardChange)
159 | )
160 | },
161 |
162 | componentWillUnmount() {
163 | this.subscriptions.forEach(sub => sub.remove())
164 | },
165 |
166 | render(): React.Element {
167 | // $FlowFixMe(>=0.41.0)
168 | const { behavior, children, style, ...props } = this.props
169 | const bottomHeight = this.props.enabled ? this.state.bottom : 0
170 | switch (behavior) {
171 | case "height":
172 | let heightStyle
173 | if (this.frame) {
174 | // Note that we only apply a height change when there is keyboard present,
175 | // i.e. this.state.bottom is greater than 0. If we remove that condition,
176 | // this.frame.height will never go back to its original value.
177 | // When height changes, we need to disable flex.
178 | heightStyle = { height: this.frame.height - bottomHeight, flex: 0 }
179 | }
180 | return (
181 |
187 | {children}
188 |
189 | )
190 |
191 | case "position":
192 | const positionStyle = { bottom: bottomHeight }
193 | const { contentContainerStyle } = this.props
194 |
195 | return (
196 |
202 |
203 | {children}
204 |
205 |
206 | )
207 |
208 | case "padding":
209 | const paddingStyle = { paddingBottom: bottomHeight }
210 | return (
211 |
217 | {children}
218 |
219 | )
220 |
221 | default:
222 | return (
223 |
229 | {children}
230 |
231 | )
232 | }
233 | }
234 | })
235 |
236 | module.exports = ParcelKeyboardView
237 |
--------------------------------------------------------------------------------
/src/component/keyboardMethods.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import SecurityKeyboard from "./securityKeyboardBase"
4 |
5 | class SecurityKeyboardMethods extends SecurityKeyboard {
6 | constructor(props) {
7 | super(props)
8 | }
9 | //清除内容(删除全部文字别名)
10 | clear() {
11 | this.removeAll()
12 | }
13 | //是否获得焦点
14 | isFocused() {
15 | if (this.state.cursorLock) {
16 | return false
17 | } else {
18 | return true
19 | }
20 | }
21 | //失去焦点
22 | blur() {
23 | this.hide()
24 | }
25 | //获得焦点
26 | focus() {
27 | this.show()
28 | }
29 | }
30 | export default SecurityKeyboardMethods
31 |
--------------------------------------------------------------------------------
/src/component/securityKeyboardBase.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import {
3 | View,
4 | Text,
5 | TouchableOpacity,
6 | Modal,
7 | Icon,
8 | TouchableHighlight,
9 | DeviceEventEmitter,
10 | Image,
11 | UIManager,
12 | findNodeHandle,
13 | Keyboard,
14 | Dimensions
15 | } from "react-native"
16 | import styles from "../style/securityKeyboard"
17 | import SecurityKeyboardInput from "./securityKeyboardInput"
18 | import { keyboardJSON } from "../resource/json/keyboard"
19 | import PropTypes from "prop-types"
20 | const { height } = Dimensions.get("window")
21 |
22 | class SecurityKeyboard extends Component {
23 | static propTypes = {
24 | key: PropTypes.string,
25 | keyboardHeader: PropTypes.element, //配置键盘头部
26 | value: PropTypes.any, //内容
27 | placeholder: PropTypes.string, //提示文字
28 | placeholderTextColor: PropTypes.string, //提示文字颜色
29 | disabled: PropTypes.bool, //是否可以输入
30 | caretHidden: PropTypes.bool, //是否隐藏光标
31 | secureTextEntry: PropTypes.bool, //是否开启密码模式
32 | style: PropTypes.any, //外壳样式
33 | valueStyle: PropTypes.any, //内容样式
34 | cursorStyle: PropTypes.any, // 闪动光标样式
35 | secureTextStyle: PropTypes.any, // 密码框圆点光标样式
36 | regs: PropTypes.func, //校验函数
37 | onChangeText: PropTypes.func, //内容更改后的回调
38 | onFocus: PropTypes.func, //得到焦点后的回调
39 | onBlur: PropTypes.func //失去焦点后的回调
40 | }
41 |
42 | constructor(props) {
43 | super(props)
44 | this.modalVisible = false
45 | this.upCase = this.props.keyboardType
46 | this.state = {
47 | // modalVisible: false, //弹窗锁
48 | caretHidden: false, //隐藏光标
49 | secureTextEntry: false, //密码模式
50 | keyboardType: this.props.keyboardType,
51 | valueArr: this.props.value || [], //文字,
52 | currentArr: [],
53 | //键盘数组
54 | cursorLock: true //光标锁
55 | }
56 | this.numArr = [...keyboardJSON.numArr] //键盘数组
57 | this.symbolArr = [...keyboardJSON.symbolArr] //键盘数组
58 | this.stringArr = [...keyboardJSON.stringArr]
59 | this.stringArrUp = [...keyboardJSON.stringArrUp]
60 | }
61 |
62 | componentDidMount() {
63 | let that = this
64 | //设置密码模式
65 | this.props.secureTextEntry &&
66 | this.setState({
67 | secureTextEntry: true
68 | })
69 | this.props.caretHidden &&
70 | this.setState({
71 | caretHidden: true
72 | })
73 | this.changeKey()
74 | this.keyboardDidShowListener = Keyboard.addListener(
75 | "keyboardDidShow",
76 | this._keyboardDidShow
77 | )
78 | this.keyboardDidHideListener = Keyboard.addListener(
79 | "keyboardDidHide",
80 | this._keyboardDidHide
81 | )
82 | }
83 |
84 | _keyboardDidShow = () => {
85 | this.systemKeyboard = true
86 | }
87 |
88 | _keyboardDidHide = () => {
89 | this.systemKeyboard = false
90 | }
91 |
92 | componentWillUnmount() {
93 | this.keyboardDidShowListener.remove()
94 | this.keyboardDidHideListener.remove()
95 | }
96 |
97 | changeKey() {
98 | this.numArr = this.props.random ? this.shuffle(this.numArr) : this.numArr
99 | this.setChangeDateNum()
100 | // currentNumArr.push(...['.', 'del', 'ABC', '!?#']);
101 | this.stringArr = this.props.random
102 | ? this.shuffle(this.stringArr)
103 | : this.stringArr
104 | // 小写数组转大写69
105 | const rule = /[a-z]/i
106 | const stringArrCaps = this.stringArr.map(item => {
107 | if (rule.test(item)) {
108 | return item.toLocaleUpperCase()
109 | }
110 | })
111 | this.setChangeDateString(this.stringArr, false)
112 | // 符号数据
113 | this.symbolArr = this.props.random
114 | ? this.shuffle(this.symbolArr)
115 | : this.symbolArr
116 | this.setChangeDateSymbol()
117 | this.stringArrUp = stringArrCaps
118 | this.setChangeDateString(this.stringArrUp, true)
119 | }
120 |
121 | shouldComponentUpdate(nextProps, nextState) {
122 | //去除不必要的渲染
123 | if (
124 | this.modalVisible != nextState.modalVisible ||
125 | this.state.cursorLock != nextState.cursorLock ||
126 | this.props.disabled != nextProps.disabled
127 | ) {
128 | return true
129 | }
130 | return false
131 | }
132 |
133 | //显示键盘
134 | show() {
135 | if (this.systemKeyboard) {
136 | Keyboard.dismiss()
137 | return
138 | }
139 | this.modalVisible = true
140 | this.setState({
141 | cursorLock: false
142 | })
143 | this.onFocus()
144 | let timer = setTimeout(() => {
145 | UIManager.measure(
146 | findNodeHandle(this.refs.keyboardWrap),
147 | (x, y, widths, heights) => {
148 | DeviceEventEmitter.emit("_keyboardDidShow", {
149 | endCoordinates: {
150 | screenX: 0,
151 | height: 285,
152 | width: widths,
153 | screenY: height - heights
154 | },
155 | isLoadKeyBoard: true
156 | })
157 | }
158 | )
159 | clearTimeout(timer)
160 | }, 50)
161 | }
162 |
163 | //隐藏键盘
164 | hide() {
165 | this.modalVisible = false
166 | this.setState({
167 | cursorLock: true,
168 | keyboardType: this.props.keyboardType
169 | })
170 | this.onBlur()
171 | DeviceEventEmitter.emit("_keyboardDidHide", null)
172 | }
173 |
174 | //发送事件 附带input内容
175 | inputEvent(value) {
176 | DeviceEventEmitter.emit(this.props.keyName || "keyboardListener", value)
177 | this.onChangeText(value)
178 | }
179 |
180 | //回调onChangeText
181 | onChangeText(value) {
182 | if (value == undefined || value == null) {
183 | return false
184 | }
185 | this.props.onChangeText && this.props.onChangeText(value.join(""))
186 | }
187 |
188 | //得到焦点
189 | onFocus() {
190 | this.props.onFocus && this.props.onFocus()
191 | }
192 |
193 | //失去焦点
194 | onBlur() {
195 | this.props.onBlur && this.props.onBlur()
196 | }
197 |
198 | //校验文字
199 | regs(valueArr) {
200 | if (!this.props.regs) {
201 | return valueArr
202 | }
203 | valueArr = this.props.regs(valueArr.join(""))
204 | valueArr = valueArr.split("")
205 | return valueArr
206 | }
207 |
208 | //增加文字
209 | add(value) {
210 | let valueArr = this.state.valueArr
211 | valueArr.push(value)
212 | if (valueArr == "" || valueArr == undefined || valueArr == null) {
213 | return
214 | }
215 | valueArr = this.regs(valueArr)
216 | this.setState({
217 | valueArr: valueArr
218 | })
219 | this.inputEvent(valueArr)
220 | }
221 |
222 | //删除文字
223 | remove() {
224 | let valueArr = this.state.valueArr
225 | if (valueArr.length == 0) {
226 | return
227 | }
228 | valueArr.pop()
229 | this.setState({
230 | valueArr: valueArr
231 | })
232 | this.inputEvent(valueArr)
233 | }
234 |
235 | //长按删除
236 | removeAll() {
237 | let valueArr = this.state.valueArr
238 | if (valueArr.length == 0) {
239 | return
240 | }
241 | valueArr = []
242 | this.setState({
243 | valueArr: valueArr
244 | })
245 | this.inputEvent(valueArr)
246 | }
247 |
248 | // 乱序
249 | shuffle(a) {
250 | let len = a.length
251 | for (let i = 0; i < len - 1; i++) {
252 | let index = parseInt(Math.random() * (len - i))
253 | let temp = a[index]
254 | a[index] = a[len - i - 1]
255 | a[len - i - 1] = temp
256 | }
257 | return a
258 | }
259 |
260 | //图片按钮
261 | addItemImageView(index, itemParent, sty, path, fun, funlong) {
262 | return (
263 |
270 | {typeof path !== "string" && (
271 |
272 | )}
273 |
274 | )
275 | }
276 |
277 | //文字的按钮
278 | addItemTextView(index, parentSty, sty, content, fun) {
279 | return (
280 |
286 | {content}
287 |
288 | )
289 | }
290 | //改变数字的数据
291 | setChangeDateNum() {
292 | let arr = this.props.imageArr
293 |
294 | this.numArr.splice(
295 | this.numArr.length - 1,
296 | 0,
297 | arr ? arr.back_image : require("../resource/images/back.png")
298 | )
299 | this.numArr.push(
300 | arr ? arr.delete_image : require("../resource/images/icon-delete.png")
301 | )
302 | }
303 |
304 | //改变字母的数据
305 | setChangeDateString(stringArr, isUp) {
306 | let arr = this.props.imageArr
307 | if (isUp) {
308 | stringArr.splice(
309 | 19,
310 | 0,
311 | arr
312 | ? arr.transform2_image
313 | : require("../resource/images/transform2.png")
314 | )
315 | } else {
316 | stringArr.splice(
317 | 19,
318 | 0,
319 | arr ? arr.transform_image : require("../resource/images/transform.png")
320 | )
321 | }
322 | stringArr.push(
323 | arr ? arr.delete_image : require("../resource/images/icon-delete.png")
324 | )
325 | stringArr.push("123")
326 | stringArr.push(
327 | arr ? arr.space_image : require("../resource/images/space.png")
328 | )
329 | stringArr.push("#+=")
330 | }
331 |
332 | //改变符号的数据
333 | setChangeDateSymbol() {
334 | let arr = this.props.imageArr
335 | this.symbolArr.push(
336 | arr ? arr.back_image : require("../resource/images/back.png")
337 | )
338 | this.symbolArr.push(
339 | arr ? arr.space_image : require("../resource/images/space.png")
340 | )
341 | this.symbolArr.push(
342 | arr ? arr.delete_image : require("../resource/images/icon-delete.png")
343 | )
344 | }
345 |
346 | renderNum() {
347 | // Determine the keyboard type
348 | if (this.state.keyboardType === "number") {
349 | return this.addOrientationView(this.numArr, 3, this._addNumView)
350 | } else if (this.state.keyboardType === "string") {
351 | return this.addOrientationView(this.stringArr, 9, this._addStringView)
352 | } else if (this.state.keyboardType === "symbol") {
353 | return this.addOrientationView(this.symbolArr, 10, this._addStringSymbol)
354 | } else if (this.state.keyboardType === "upString") {
355 | return this.addOrientationView(this.stringArrUp, 9, this._addStringView)
356 | }
357 | }
358 |
359 | //添加横向视图
360 | addOrientationView(numArr, addNum, verticalView) {
361 | return numArr.map((item, index) => {
362 | if (index % addNum == 0) {
363 | return (
364 |
365 | {verticalView(index, addNum, numArr)}
366 |
367 | )
368 | }
369 | })
370 | }
371 |
372 | //渲染数字键盘
373 | _addNumView = (flag, addNum, numArr) => {
374 | return numArr.slice(flag, flag + addNum).map((item, index) => {
375 | let icon = styles.deleteIcon
376 | if (flag + index == 9) {
377 | icon = styles.backIcon
378 | }
379 | if (flag + index == 9 || flag + index == 11) {
380 | return this.addItemImageView(
381 | index,
382 | [styles.itemNumParentText, styles.itemNumParentImage],
383 | icon,
384 | item,
385 | () => {
386 | if (index == 0) {
387 | this.setState({ keyboardType: this.upCase })
388 | }
389 | if (index == 2) {
390 | this.remove.bind(this)()
391 | }
392 | },
393 | () => {
394 | if (index == 2) {
395 | this.removeAll.bind(this)()
396 | }
397 | }
398 | )
399 | }
400 | return this.addItemTextView(
401 | index,
402 | styles.itemNumParentText,
403 | styles.numText,
404 | item,
405 | this.add.bind(this, item)
406 | )
407 | })
408 | }
409 |
410 | //渲染字母键盘
411 | _addStringView = (flag, addNum, numArr) => {
412 | // 改变列的数量
413 | if (flag == 0) {
414 | addNum++
415 | }
416 | if (flag == 9 || flag == 18 || flag == 27) {
417 | flag++
418 | }
419 |
420 | return numArr.slice(flag, flag + addNum).map((item, index) => {
421 | let icon = styles.deleteIcon
422 | if (flag + index == 19) {
423 | icon = styles.transformIcon
424 | }
425 | if (flag + index == 19 || flag + index == 27) {
426 | // 设置转换按钮和 删除按钮的样式
427 | return this.addItemImageView(
428 | index,
429 | styles.itemStringParentImage,
430 | icon,
431 | item,
432 | () => {
433 | if (index == 0) {
434 | this.upCase =
435 | this.state.keyboardType == "string" ? "upString" : "string"
436 | this.setState({ keyboardType: this.upCase })
437 | }
438 | if (index == 8) {
439 | this.remove.bind(this)()
440 | }
441 | },
442 | () => {
443 | if (index == 8) {
444 | this.removeAll.bind(this)()
445 | }
446 | }
447 | )
448 | }
449 | if (flag + index == 29) {
450 | // 设置空格
451 | return this.addItemImageView(
452 | index,
453 | styles.itemStringParentSpace,
454 | styles.spaceIcon,
455 | item,
456 | this.add.bind(this, " ")
457 | )
458 | }
459 | let parent = styles.itemStringParentText
460 | let text = styles.numText
461 | if (flag + index == 10) {
462 | parent = styles.itemStringParentText2
463 | } // 设置第二行开始左边间距
464 | if (flag + index == 18) {
465 | parent = styles.itemStringParentText3
466 | } // 设置第二行结束右边间距
467 | if (flag + index == 28 || flag + index == 30) {
468 | parent = styles.itemStringParentText4 //设置数字按钮,符号按钮的样式
469 | text = styles.symbolText
470 | }
471 |
472 | return this.addItemTextView(index, parent, text, item, () => {
473 | if (index + flag == 28) {
474 | this.setState({ keyboardType: "number" })
475 | } else if (index + flag == 30) {
476 | this.setState({ keyboardType: "symbol" })
477 | } else {
478 | this.add.bind(this, item)()
479 | }
480 | })
481 | })
482 | }
483 |
484 | //渲染符号键盘
485 | _addStringSymbol = (flag, addNum, numArr) => {
486 | return numArr.slice(flag, flag + addNum).map((item, index) => {
487 | let parent = styles.itemStringParentText4
488 | let icon = styles.deleteIcon
489 | if (flag + index == 31) {
490 | //设置返回键、空格、删除键的样式
491 | parent = styles.itemStringParentSpace
492 | icon = styles.spaceIcon
493 | }
494 | if (flag + index == 30) {
495 | icon = styles.backIcon
496 | }
497 |
498 | if (flag == 30) {
499 | return this.addItemImageView(
500 | index,
501 | parent,
502 | icon,
503 | item,
504 | () => {
505 | if (index == 0) {
506 | this.setState({ keyboardType: this.upCase })
507 | }
508 | if (index == 1) {
509 | this.add.bind(this, " ")()
510 | }
511 | if (index == 2) {
512 | this.remove.bind(this)()
513 | }
514 | },
515 | () => {
516 | if (index == 2) {
517 | this.removeAll.bind(this)()
518 | }
519 | }
520 | )
521 | }
522 | return this.addItemTextView(
523 | index,
524 | styles.itemStringParentText,
525 | styles.numText,
526 | item,
527 | this.add.bind(this, item)
528 | )
529 | })
530 | }
531 |
532 | render() {
533 | return (
534 |
535 |
550 | {}}
556 | >
557 |
558 |
569 |
570 |
571 | {this.props.keyboardHeader ? (
572 | this.props.keyboardHeader()
573 | ) : (
574 |
578 | )}
579 |
583 |
589 |
590 |
591 | {this.renderNum()}
592 |
593 |
594 |
595 |
596 | )
597 | }
598 | }
599 |
600 | export default SecurityKeyboard
601 |
--------------------------------------------------------------------------------
/src/component/securityKeyboardInput.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Keyboard input component base on hook.
4 | * Using Animated makes cursor flashing.
5 | * @Author supervons
6 | */
7 | import React, { useEffect, useState } from "react"
8 | import {
9 | Animated,
10 | DeviceEventEmitter,
11 | Text,
12 | TouchableOpacity,
13 | View
14 | } from "react-native"
15 |
16 | import styles from "../style/securityKeyboardInput"
17 |
18 | function SecurityKeyboardInput(props) {
19 | const [fadeAnim] = useState(new Animated.Value(0))
20 | const [valueArr, setValueArr] = useState(props.value || [])
21 |
22 | useEffect(() => {
23 | const keyboardListener = DeviceEventEmitter.addListener(
24 | props.keyName || "keyboardListener",
25 | data => {
26 | setValueArr(data)
27 | }
28 | )
29 | // Perform the animation
30 | animation()
31 | // Execute before each rendering effect is executed
32 | return function cleanup() {
33 | keyboardListener.remove()
34 | }
35 | }, [])
36 |
37 | /**
38 | * Listen for the cursor and reset the animation Hook.
39 | */
40 | useEffect(() => {
41 | if (!props.cursorLock) {
42 | animation()
43 | }
44 | }, [props.cursorLock])
45 |
46 | function animation() {
47 | Animated.loop(
48 | Animated.sequence([
49 | Animated.timing(fadeAnim, {
50 | toValue: 1,
51 | duration: 600,
52 | useNativeDriver: true
53 | }),
54 | Animated.timing(fadeAnim, {
55 | toValue: 0,
56 | duration: 600,
57 | useNativeDriver: true
58 | })
59 | ]),
60 | {
61 | iterations: 400
62 | }
63 | ).start()
64 | }
65 |
66 | function renderValue() {
67 | return valueArr.map((item, index) => {
68 | return (
69 |
70 | {props.secureTextEntry ? "●" : item}
71 |
72 | )
73 | })
74 | }
75 |
76 | //显示键盘
77 | function show() {
78 | if (props.disabled) {
79 | return
80 | }
81 | props.show()
82 | }
83 |
84 | /**
85 | * According props.secureTextEntry to displays user input or displays as a dot ●
86 | */
87 | return (
88 |
89 |
90 |
98 | {renderValue()}
99 |
100 | {valueArr.length == 0 ? (
101 |
108 | {props.placeholder || "请输入内容"}
109 |
110 | ) : null}
111 | {!props.cursorLock && !props.caretHidden ? (
112 |
113 | |
114 |
115 | ) : null}
116 |
117 |
118 | )
119 | }
120 |
121 | export default SecurityKeyboardInput
122 |
--------------------------------------------------------------------------------
/src/resource/images/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supervons/react-native-supervons-custom-keyboard/a175b6ccca0865be37eb0cecae217b7284961b2c/src/resource/images/back.png
--------------------------------------------------------------------------------
/src/resource/images/icon-delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supervons/react-native-supervons-custom-keyboard/a175b6ccca0865be37eb0cecae217b7284961b2c/src/resource/images/icon-delete.png
--------------------------------------------------------------------------------
/src/resource/images/icon-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supervons/react-native-supervons-custom-keyboard/a175b6ccca0865be37eb0cecae217b7284961b2c/src/resource/images/icon-down.png
--------------------------------------------------------------------------------
/src/resource/images/space.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supervons/react-native-supervons-custom-keyboard/a175b6ccca0865be37eb0cecae217b7284961b2c/src/resource/images/space.png
--------------------------------------------------------------------------------
/src/resource/images/text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supervons/react-native-supervons-custom-keyboard/a175b6ccca0865be37eb0cecae217b7284961b2c/src/resource/images/text.png
--------------------------------------------------------------------------------
/src/resource/images/transform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supervons/react-native-supervons-custom-keyboard/a175b6ccca0865be37eb0cecae217b7284961b2c/src/resource/images/transform.png
--------------------------------------------------------------------------------
/src/resource/images/transform2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supervons/react-native-supervons-custom-keyboard/a175b6ccca0865be37eb0cecae217b7284961b2c/src/resource/images/transform2.png
--------------------------------------------------------------------------------
/src/resource/json/keyboard.js:
--------------------------------------------------------------------------------
1 | export const keyboardJSON = {
2 | numArr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
3 | symbolArr: [
4 | "&",
5 | '"',
6 | ";",
7 | "^",
8 | ",",
9 | "|",
10 | "$",
11 | "*",
12 | ":",
13 | "(",
14 | ")",
15 | "{",
16 | "}",
17 | "[",
18 | "]",
19 | "-",
20 | "+",
21 | "=",
22 | "_",
23 | "\\",
24 | "/",
25 | "!",
26 | "?",
27 | "~",
28 | "#",
29 | "%",
30 | ".",
31 | "@",
32 | "¥",
33 | "€"
34 | ],
35 | stringArr: [
36 | "q",
37 | "w",
38 | "e",
39 | "r",
40 | "t",
41 | "y",
42 | "u",
43 | "i",
44 | "o",
45 | "p",
46 | "a",
47 | "s",
48 | "d",
49 | "f",
50 | "g",
51 | "h",
52 | "j",
53 | "k",
54 | "l",
55 | "z",
56 | "x",
57 | "c",
58 | "v",
59 | "b",
60 | "n",
61 | "m"
62 | ],
63 | stringArrUp: [
64 | "Q",
65 | "W",
66 | "E",
67 | "R",
68 | "T",
69 | "Y",
70 | "U",
71 | "I",
72 | "O",
73 | "P",
74 | "A",
75 | "S",
76 | "D",
77 | "F",
78 | "G",
79 | "H",
80 | "J",
81 | "K",
82 | "L",
83 | "Z",
84 | "X",
85 | "C",
86 | "V",
87 | "B",
88 | "N",
89 | "M"
90 | ]
91 | }
92 |
--------------------------------------------------------------------------------
/src/style/securityKeyboard.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, Platform, Dimensions } from "react-native"
2 |
3 | let basePx = Platform.OS === "ios" ? 750 : 720
4 | let { width } = Dimensions.get("window")
5 |
6 | function px2dp(px) {
7 | return (px / basePx) * width
8 | }
9 |
10 | const Styles = StyleSheet.create({
11 | textInputWrap: {
12 | borderWidth: 1,
13 | height: 40,
14 | borderColor: "#999999",
15 | borderRadius: 5,
16 | flexDirection: "row",
17 | alignItems: "center",
18 | paddingLeft: px2dp(10)
19 | },
20 | cursorWrap: {
21 | height: 40,
22 | flexDirection: "row",
23 | alignItems: "center"
24 | },
25 | cursor: {
26 | fontSize: 30,
27 | fontWeight: "300"
28 | },
29 | root: {
30 | flex: 1,
31 | flexDirection: "column",
32 | alignItems: "center",
33 | justifyContent: "center"
34 | },
35 | defaultHeader: {
36 | flex: 1,
37 | flexDirection: "row",
38 | alignItems: "center",
39 | justifyContent: "center"
40 | },
41 | headerWrap: {
42 | height: 42,
43 | flexDirection: "row",
44 | alignItems: "center",
45 | justifyContent: "center"
46 | },
47 | headerText: {
48 | fontSize: 14,
49 | color: "#5FBF9F"
50 | },
51 | headerImage: {
52 | width: px2dp(260),
53 | resizeMode: "contain"
54 | },
55 | closeIconWrap: {
56 | position: "absolute",
57 | width: 50,
58 | height: 50,
59 | right: 0,
60 | alignItems: "center",
61 | justifyContent: "center"
62 | },
63 | closeIcon: {
64 | width: px2dp(30),
65 | height: px2dp(30),
66 | resizeMode: "contain"
67 | },
68 | removeIcon: {
69 | width: px2dp(50),
70 | resizeMode: "contain"
71 | },
72 | keyboardWrap: {
73 | height: 285,
74 | position: "absolute",
75 | bottom: 0,
76 | left: 0,
77 | right: 0,
78 | backgroundColor: "#ffffff",
79 | borderWidth: 1,
80 | borderTopColor: "#cccccc"
81 | },
82 |
83 | numText: {
84 | color: "#000000",
85 | fontSize: 24
86 | },
87 | symbolText: {
88 | color: "#000000",
89 | fontSize: 20
90 | },
91 | bottomWrap: {
92 | flexDirection: "row",
93 | justifyContent: "space-around"
94 | },
95 |
96 | deleteIcon: {
97 | width: px2dp(46),
98 | height: px2dp(32)
99 | },
100 | transformIcon: {
101 | width: px2dp(40),
102 | height: px2dp(45)
103 | },
104 | backIcon: {
105 | width: px2dp(40),
106 | height: px2dp(35)
107 | },
108 | spaceIcon: {
109 | marginTop: 20,
110 | width: px2dp(200),
111 | height: 200
112 | },
113 | itemNumParentImage: {
114 | backgroundColor: "#BDC5D3"
115 | },
116 |
117 | itemNumParentText: {
118 | alignItems: "center",
119 | justifyContent: "center",
120 | backgroundColor: "#ffffff",
121 | borderRadius: 8,
122 | margin: 4,
123 | flex: 1
124 | },
125 | itemStringParentText: {
126 | alignItems: "center",
127 | justifyContent: "center",
128 | backgroundColor: "#ffffff",
129 | borderRadius: 6,
130 | marginHorizontal: 3,
131 | marginVertical: 6,
132 | flex: 1
133 | },
134 | itemStringParentText2: {
135 | alignItems: "center",
136 | justifyContent: "center",
137 | backgroundColor: "#ffffff",
138 | borderRadius: 6,
139 | marginLeft: 12,
140 | marginRight: 3,
141 | marginVertical: 6,
142 | flex: 1
143 | },
144 | itemStringParentText3: {
145 | alignItems: "center",
146 | justifyContent: "center",
147 | backgroundColor: "#ffffff",
148 | borderRadius: 6,
149 | marginLeft: 3,
150 | marginRight: 12,
151 | marginVertical: 6,
152 | flex: 1
153 | },
154 | itemStringParentText4: {
155 | alignItems: "center",
156 | justifyContent: "center",
157 | backgroundColor: "#BDC5D3",
158 | borderRadius: 6,
159 | marginHorizontal: 3,
160 | marginVertical: 6,
161 | width: px2dp(120)
162 | },
163 |
164 | itemStringParentImage: {
165 | alignItems: "center",
166 | justifyContent: "center",
167 | backgroundColor: "#BDC5D3",
168 | borderRadius: 6,
169 | marginHorizontal: 3,
170 | marginVertical: 6,
171 | width: px2dp(85)
172 | },
173 | itemStringParentSpace: {
174 | alignItems: "center",
175 | justifyContent: "center",
176 | backgroundColor: "#BDC5D3",
177 | borderRadius: 6,
178 | marginHorizontal: 3,
179 | marginVertical: 6,
180 | flex: 1
181 | },
182 |
183 | orientation: {
184 | flexDirection: "row",
185 | justifyContent: "space-around",
186 | flex: 1
187 | },
188 |
189 | lengthwaysClass: {
190 | flexDirection: "column",
191 | justifyContent: "space-around",
192 | flex: 1,
193 | backgroundColor: "#E3E5EC",
194 | padding: 2
195 | }
196 | })
197 |
198 | export default Styles
199 |
--------------------------------------------------------------------------------
/src/style/securityKeyboardInput.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, Platform, Dimensions } from "react-native"
2 |
3 | let basePx = Platform.OS === "ios" ? 750 : 720
4 | let { width } = Dimensions.get("window")
5 | function px2dp(px) {
6 | return (px / basePx) * width
7 | }
8 |
9 | const styles = StyleSheet.create({
10 | view: {
11 | width: "100%",
12 | paddingRight: 20
13 | },
14 | textInputWrap: {
15 | height: 40,
16 | flexDirection: "row",
17 | alignItems: "center"
18 | },
19 | cursorWrap: {
20 | height: 40,
21 | flexDirection: "row",
22 | alignItems: "center"
23 | },
24 | cursor: {
25 | fontSize: 25,
26 | color: "#4970EA",
27 | left: px2dp(-2)
28 | },
29 | placeholder: {
30 | color: "#C4C4C4",
31 | fontSize: 20,
32 | position: "absolute",
33 | left: 13
34 | },
35 | value: {
36 | fontSize: 16
37 | }
38 | })
39 |
40 | export default styles
41 |
--------------------------------------------------------------------------------