React Native Unsigned Input: Giải pháp nhập liệu không dấu cho bàn phím Vietnamese và các ngôn ngữ có dấu khác
1. Giới thiệu về plugin
-
Trong các ứng dụng di động, việc nhập liệu chữ không dấu là một yêu cầu phổ biến. Tuy nhiên, việc xử lý nhập liệu này trong React Native có thể gặp một số khó khăn. Để giải quyết vấn đề này, mình đã phát triển thư viện này.
-
Plugin này cung cấp một thành phần nhập liệu dựa trên TextInput của React Native, giúp giới hạn việc nhập liệu chỉ gồm các ký tự không dấu. Điều này đồng nghĩa với việc người dùng chỉ có thể nhập các giá trị số dương và không thể nhập các ký tự không phải số.
2. Cách sử dụng plugin
Để bắt đầu sử dụng plugin này, bạn cần cài đặt nó vào dự án React Native của bạn:
npm install @tdduydev/react-native-unsigned-input
Sau khi cài đặt xong, bạn có thể sử dụng thành phần UnsignedInput trong dự án của mình như sau:
import React from 'react';
import { View } from 'react-native';
import UnsignedInput from '@tdduydev/react-native-unsigned-input';
const App = () => {
return (
<View>
<UnsignedInput />
</View>
);
};
export default App;
3. Ưu điểm của plugin
- Dễ dàng sử dụng: Chỉ cần cài đặt và sử dụng thành phần UnsignedInput trong dự án của bạn.
- Tích hợp nhanh chóng: Plugin được phát triển dựa trên TextInput của React Native, giúp tích hợp dễ dàng vào các ứng dụng hiện có.
- Tiết kiệm thời gian: Giảm bớt công việc liên quan đến xử lý nhập liệu chữ không dấu.
4. Nhược điểm
- Chưa hỗ trợ đa ngôn ngữ: Nếu ứng dụng của bạn cần hỗ trợ đa ngôn ngữ, bạn cần tự thêm tính năng này.
- Tùy chỉnh giới hạn: Plugin chỉ hỗ trợ nhập liệu chữ không dấu, nếu bạn muốn tùy chỉnh giới hạn nhập liệu (ví dụ: chỉ cho phép nhập số lớn hơn 10), bạn sẽ phải thực hiện điều này bằng cách sửa mã nguồn hoặc thêm các xử lý bổ sung.
5. Một số ví dụ về tùy chỉnh plugin
Ví dụ 1: Thêm placeholder và kiểu nhập liệu
import React from 'react';
import { View } from 'react-native';
import UnsignedInput from '@tdduydev/react-native-unsigned-input';
const App = () => {
return (
<View>
<UnsignedInput
placeholder="Nhập số không dấu"
keyboardType="number-pad"
/>
</View>
);
};
export default App;
Ví dụ 2: Thêm sự kiện thay đổi giá trị và xử lý nhập liệu
import React, { useState } from 'react';
import { View, Text } from 'react-native';
import UnsignedInput from '@tdduydev/react-native-unsigned-input';
const App = () => {
const [value, setValue] = useState('');
const handleChange = (text) => {
setValue(text);
};
return (
<View>
<UnsignedInput onChangeText={handleChange} />
<Text>Giá trị nhập: {value}</Text>
</View>
);
};
export default App;
6. Phân tích sâu về code của native ios và native android
native ios
//
// ReactNativeUnsignedDelegate.m
// UnsignedInput
//
// Created by DUY TRAN on 27/03/2023.
// Copyright © 2023 Facebook. All rights reserved.
//
#import <UIKit/UIKit.h>
@protocol InputListener <UITextFieldDelegate>
@end
@interface ReactNativeUnsignedDelegate : NSObject <UITextFieldDelegate>
@property (nonatomic, weak) id<InputListener> listener;
@property (nonatomic, copy) void (^onChange)(UITextField *textField, NSString *value);
- (instancetype)initWithListener:(id<InputListener>)listener
onChange:(void (^)(UITextField *textField, NSString *value))onChange;
@end
@implementation ReactNativeUnsignedDelegate
- (instancetype)initWithListener:(id<InputListener>)listener
onChange:(void (^)(UITextField *textField, NSString *value))onChange {
self = [super init];
if (self) {
_listener = listener;
_onChange = onChange;
}
return self;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Chuẩn hóa chuỗi nhập vào bằng cách loại bỏ dấu tiếng Việt và khoảng trắng
NSString *normalizedString = [[[string decomposedStringWithCanonicalMapping] stringByFoldingWithOptions:NSDiacriticInsensitiveSearch locale:[NSLocale currentLocale]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
// Thay thế chuỗi nhập vào bằng chuỗi đã được chuẩn hóa
textField.text = [textField.text stringByReplacingCharactersInRange:range withString:normalizedString];
self.onChange(textField,textField.text);
return NO;
}
// MARK: default RNTextInput handlers
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
return [self.listener respondsToSelector:@selector(textFieldShouldBeginEditing:)] ? [self.listener textFieldShouldBeginEditing:textField] : YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
[self.listener textFieldDidBeginEditing:textField];
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
return [self.listener respondsToSelector:@selector(textFieldShouldEndEditing:)] ? [self.listener textFieldShouldEndEditing:textField] : YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
[self.listener textFieldDidEndEditing:textField];
}
- (void)textFieldDidEndEditing:(UITextField *)textField reason:(UITextFieldDidEndEditingReason)reason API_AVAILABLE(ios(10.0)) {
if ([self.listener respondsToSelector:@selector(textFieldDidEndEditing:reason:)]) {
[self.listener textFieldDidEndEditing:textField reason:reason];
} else {
[self.listener textFieldDidEndEditing:textField];
}
}
@end
- Khai báo và import thư viện:
#import <UIKit/UIKit.h>
Thư viện UIKit
được import để sử dụng các thành phần UI của iOS.
- Khai báo giao diện InputListener:
@protocol InputListener <UITextFieldDelegate>
@end
Giao diện InputListener
kế thừa từ UITextFieldDelegate
, cho phép xử lý các sự kiện liên quan đến UITextField
.
- Khai báo lớp
ReactNativeUnsignedDelegate
:
@interface ReactNativeUnsignedDelegate : NSObject <UITextFieldDelegate>
@property (nonatomic, weak) id<InputListener> listener;
@property (nonatomic, copy) void (^onChange)(UITextField *textField, NSString *value);
- (instancetype)initWithListener:(id<InputListener>)listener
onChange:(void (^)(UITextField *textField, NSString *value))onChange;
@end
Lớp ReactNativeUnsignedDelegate
kế thừa từ NSObject
và tuân thủ giao diện UITextFieldDelegate
. Lớp này có hai thuộc tính
listener
: Một đối tượng tuân thủ giao diệnInputListener
, giúp xử lý các sự kiện củaUITextField
.onChange
: Một block được gọi khi giá trị củaUITextField
thay đổi.
Lớp này cũng có một phương thức khởi tạo initWithListener:onChange
.
- Thực thi lớp ReactNativeUnsignedDelegate:
@implementation ReactNativeUnsignedDelegate
Phần này chứa các phương thức được gọi khi xử lý sự kiện của UITextField
.
- Phương thức khởi tạo
initWithListener:onChange
:
- (instancetype)initWithListener:(id<InputListener>)listener
onChange:(void (^)(UITextField *textField, NSString *value))onChange {
self = [super init];
if (self) {
_listener = listener;
_onChange = onChange;
}
return self;
}
Phương thức này khởi tạo một đối tượng của lớp ReactNativeUnsignedDelegate
với đối listener
và block onChange
.
- Phương thức
textField:shouldChangeCharactersInRange:replacementString
:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
Phương thức này được gọi khi có sự thay đổi ký tự trong UITextField
. Nó chuẩn hóa chuỗi nhập vào bằng cách loại bỏ dấu tiếng Việt và khoảng trắng, sau đó thay thế chuỗi nhập vào bằng chuỗi đã được chuẩn hóa.
- Các phương thức xử lý sự kiện của
UITextField
:
Phần này chứa các phương thức xử lý sự kiện của UITextField
,như textFieldShouldBeginEditing
, textFieldDidBeginEditing
, textFieldShouldEndEditing
, textFieldDidEndEditing
và textFieldDidEndEditing:reason
. Các phương thức này được gọi khi có các sự kiện liên quan đến việc bắt đầu và kết thúc nhập liệu của UITextField
.
Ví dụ:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
return [self.listener respondsToSelector:@selector(textFieldShouldBeginEditing:)] ? [self.listener textFieldShouldBeginEditing:textField] : YES;
}
Phương thức textFieldShouldBeginEditing
kiểm tra xem listener
có thực hiện phương thức textFieldShouldBeginEditing
hay không. Nếu có, phương thức này sẽ gọi textFieldShouldBeginEditing
của listener
, ngược lại trả về YES
.
- Kết thúc cài đặt lớp
ReactNativeUnsignedDelegate
:
Tóm lại, đoạn mã trên là một file mã nguồn Objective-C được sử dụng trong ứng dụng React Native để hỗ trợ việc nhập liệu không dấu. Nó tạo ra một lớp tuân thủ giao diện UITextFieldDelegate
để xử lý các sự kiện của UITextField
, giúp chuẩn hóa chuỗi nhập vào bằng cách loại bỏ dấu tiếng Việt và khoảng trắng.
native android
- Khai báo package và import các thư viện cần thiết:
import android.text.InputType
import android.text.TextWatcher
import android.widget.EditText
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.UIManagerModule
- Khai báo lớp
ReactNativeUnsignedInputModule
:
class ReactNativeUnsignedInputModule(private val reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext)
Lớp ReactNativeUnsignedInputModule
kế thừa từ ReactContextBaseJavaModule
và nhận một tham số reactContext
kiểu ReactApplicationContext
.
- Override phương thức
getName()
:
override fun getName(): String {
return NAME
}
Phương thức này trả về tên của module, trong trường hợp này là ReactNativeUnsignedInput
.
- Khởi tạo và quản lý
listeners
:
private val listeners = hashMapOf<String, TextWatcher?>()
listeners
là một HashMap
chứa các TextWatcher
dùng để lắng nghe sự kiện thay đổi của EditText
.
- Phương thức
applyUnsigned(reactNode: Int)
:
@ReactMethod
fun applyUnsigned(reactNode: Int) {
...
}
Phương thức này được đánh dấu là @ReactMethod
, cho phép gọi từ mã JavaScript
của React Native. Phương thức này nhận vào reactNode
là ID của EditText
cần áp dụng nhập liệu không dấu.
- Thêm
UnsignedTextWatcher
vàoEditText
:
val editText = viewRegistry.resolveView(reactNode) as EditText
val listener = UnsignedTextWatcher(editText)
listeners.set(getKey(reactNode), listener)
editText.addTextChangedListener(listener)
Phần mã này lấy ra đối tượng EditText
từ viewRegistry
, khởi tạo một UnsignedTextWatcher
, lưu vào listeners
, và thêm vào EditText
để lắng nghe sự kiện thay đổi.
- Phương thức
getKey(reactNode: Int)
:
private fun getKey(reactNode: Int): String {
return reactNode.toString()
}
Phương thức này chuyển đổi reactNode (kiểu Int)
thành chuỗi và trả về để sử dụng làm key cho listeners
.
Full-code
package com.unsignedinput
import android.text.InputType
import android.text.TextWatcher
import android.widget.EditText
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.UIManagerModule
class ReactNativeUnsignedInputModule(private val reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return NAME
}
private val listeners = hashMapOf<String, TextWatcher?>()
@ReactMethod
fun applyUnsigned(reactNode: Int) {
val uiManager = reactContext.getNativeModule(UIManagerModule::class.java)!!
uiManager.addUIBlock { viewRegistry ->
val editText = viewRegistry.resolveView(reactNode) as EditText
val listener = UnsignedTextWatcher(editText)
listeners.set(getKey(reactNode), listener)
editText.addTextChangedListener(listener)
// Add this line to set the input type to password
editText.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD)
}
}
private fun safeResolveString(map: ReadableMap, key: String): String? {
return try {
map.getString(key)
} catch (e: Exception) {
null
}
}
private fun safeResolveString(map: ReadableMap, key: String, defaultValue: String): String {
return safeResolveString(map, key) ?: defaultValue
}
private fun safeResolveInt(map: ReadableMap, key: String): Int? {
return try {
map.getInt(key)
} catch (e: Exception) {
null
}
}
private fun safeResolveInt(map: ReadableMap, key: String, defaultValue: Int): Int {
return safeResolveInt(map, key) ?: defaultValue
}
companion object {
const val NAME = "ReactNativeUnsignedInput"
}
}
Tóm lại, Nó tạo ra một lớp ReactNativeUnsignedInputModule kế thừa từ ReactContextBaseJavaModule và tuân thủ giao diện TextWatcher để xử lý các sự kiện của EditText, giúp chuẩn hóa chuỗi nhập vào bằng cách loại bỏ dấu tiếng Việt và khoảng trắng.
Demo
Như vậy, @tdduydev/react-native-unsigned-input
là một plugin hữu ích, giúp bạn dễ dàng nhập liệu không dấu trong các ứng dụng React Native. Tuy nhiên, để tận dụng tối đa plugin này, bạn cần phải tùy chỉnh và thêm các xử lý bổ sung phù hợp với yêu cầu của ứng dụng. Chúc các bạn thành công với việc sử dụng và tùy biến plugin này!
All rights reserved