Android限制EditText只能輸入中文或者指定內容的實現
最近專案中要限制EditText中只能輸入中文,之前寫過一個限制EditText只能輸入中文的實現,不過存在一些問題,而且擴充套件性不是很好,所以換了一種方法來實現.
先看一下效果圖:
具體實現
一般對EditText的操作及處理都是用addTextChangedListener方法來對EditText進行監聽,之後在監聽方法中去做處理.這裡也打算用這個種方法來做,大體的思路是監聽EditText中輸入的內容,然後將不是中文的部分清除掉,也就是置為空.所以大概應該這樣寫
mLimitEt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 1.處理輸入的內容s:清除其中不是中文的部分
...
// 2.設定處理完的s
mLimitEt.setText("處理之後的s" );
}
@Override
public void afterTextChanged(Editable s) {
}
});
處理的方法這裡先不寫,先來看一下這樣寫會出現的一個問題,執行一下,輸入一些內容會發現程式崩潰了,檢視崩潰資訊,會發現出現了StackOverflowError異常,這是什麼原因呢?帶著疑問去扒了一下原始碼(看原始碼時遇到一個問題,升級完Studio之後,發現無法檢視原始碼了,查了一些資料解決了,也有相同問題的童鞋可以參考下我寫的 Mac版Android Studio檢視不到原始碼的解決方法
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
...
// Text改變前的回撥處理
sendBeforeTextChanged(mText, 0, oldlen, text.length());
...
// Text改變中的回撥處理
sendOnTextChanged(text, 0, oldlen, textLength);
...
// Text改變後的回撥處理
sendAfterTextChanged((Editable) text);
}
在setText方法中可以看到這幾個方法,然後看一下這些方法做的處理是什麼
/**
* Not private so it can be called from an inner class without going
* through a thunk.
*/
void sendOnTextChanged(CharSequence text, int start, int before, int after) {
if (mListeners != null) {
final ArrayList<TextWatcher> list = mListeners;
final int count = list.size();
for (int i = 0; i < count; i++) {
list.get(i).onTextChanged(text, start, before, after);
}
}
if (mEditor != null) mEditor.sendOnTextChanged(start, after);
}
/**
* Not private so it can be called from an inner class without going
* through a thunk.
*/
void sendAfterTextChanged(Editable text) {
if (mListeners != null) {
final ArrayList<TextWatcher> list = mListeners;
final int count = list.size();
for (int i = 0; i < count; i++) {
list.get(i).afterTextChanged(text);
}
}
hideErrorIfUnchanged();
}
看一下這些方法,能不能發現點什麼,可以看到有一個ArrayList< TextWatcher >物件,先進行判空處理,如果這個物件中存在TextWatcher監聽,則逐條進行回撥操作.再回頭看一下之前寫的EditText中回撥方法的實現,在回撥中,對這個EditText進行了setText操作,因為EditText實現了TextWatcher的回撥介面,這樣就導致了無限迴圈 setText->onTextChanged->setText…… 最終導致程式崩潰.那該如何解決這個問題呢.其實很簡單,看一下程式碼
mLimitEt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 1.處理輸入的內容s:清除其中不是中文的部分
...
// 2.刪除監聽
mLimitEt.removeTextChangedListener(this);
// 3.設定處理完的s
mLimitEt.setText("處理之後的s");
// 4.重新新增監聽
mLimitEt.addTextChangedListener(this);
}
@Override
public void afterTextChanged(Editable s) {
}
});
在setText之前先刪除之前的回撥監聽,setText時因為沒有TextWatcher的監聽方法,所以不會出現無限迴圈的情況,當setText之後再重新添加回調監聽,這樣就避免了崩潰的產生.之後看一下清除非中文部分的實現,直接看程式碼
/**
* 清除不是中文的內容
*
* @param regex
* @return
*/
private String clearLimitStr(String regex, String str) {
return str.replaceAll("[^\u4E00-\u9FA5]", "");
}
用了String的replaceAll方法來處理輸入的內容(用了正則表示式,使用起來很簡單).在onTextChanged和afterTextChanged方法中,得到的輸入內容其實是整體的輸入內容,所以用replaceAll方法,可以去列印一下這幾個方法中的引數,這裡就不做了.看一下整體程式碼
LimitInputTextWatcher:
package com.example.junweiliu.limitinputdemo;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
/**
* Created by junweiliu on 17/1/6.
*/
public class LimitInputTextWatcher implements TextWatcher {
/**
* et
*/
private EditText et = null;
/**
* 篩選條件
*/
private String regex;
/**
* 預設的篩選條件(正則:只能輸入中文)
*/
private String DEFAULT_REGEX = "[^\u4E00-\u9FA5]";
/**
* 構造方法
*
* @param et
*/
public LimitInputTextWatcher(EditText et) {
this.et = et;
this.regex = DEFAULT_REGEX;
}
/**
* 構造方法
*
* @param et et
* @param regex 篩選條件
*/
public LimitInputTextWatcher(EditText et, String regex) {
this.et = et;
this.regex = regex;
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
String str = editable.toString();
String inputStr = clearLimitStr(regex, str);
et.removeTextChangedListener(this);
// et.setText方法可能會引起鍵盤變化,所以用editable.replace來顯示內容
editable.replace(0, editable.length(), inputStr.trim());
et.addTextChangedListener(this);
}
/**
* 清除不符合條件的內容
*
* @param regex
* @return
*/
private String clearLimitStr(String regex, String str) {
return str.replaceAll(regex, "");
}
}
為了擴充套件性,提出來了一個類,提供了兩個構造方法,如果需要限制其他的特殊內容,可以設定正則的規則.當然如果很簡單的話,用EidtText自帶的digits屬性就可以了.還有一個問題,需要注意,程式碼中沒有用et.setText方法,是因為setText方法可能引起鍵盤變化異常,所以這裡用 editable.replace(0, editable.length(), inputStr.trim());這個方法和setText方法的實現效果是一樣的.不過也需要對監聽進行處理,原因也是因為會引起無限迴圈,感興趣的童鞋可以去看一下.
完整程式碼
MainActivity:
package com.example.junweiliu.limitinputdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.EditText;
public class MainActivity extends AppCompatActivity {
/**
* et
*/
private EditText mLimitEt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLimitEt = (EditText) findViewById(R.id.et_limit);
mLimitEt.addTextChangedListener(new LimitInputTextWatcher(mLimitEt));
// 去除除了a-z A-Z與0-9和中文的其他符號
// mLimitEt.addTextChangedListener(new LimitInputTextWatcher(mLimitEt, "[^a-zA-Z0-9\u4E00-\u9FA5]"));
}
}
activity_main:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.junweiliu.limitinputdemo.MainActivity">
<!--輸入框-->
<!--android:digits="1234567890"-->
<EditText
android:id="@+id/et_limit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:hint="我是一個受限制的輸入框"/>
<!--輸入框-->
<EditText
android:layout_below="@+id/et_limit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="複製我fuzhiwo845"
android:hint=""/>
</RelativeLayout>