使用輸入框時產品常常會有一些需求,比如123456789變成123-456-789或者限制一些字元的輸入等等。很多時候都是網上搜尋就完事了,但是每次都去搜尋有點浪費時間,而且有些也不符合需求。所以自己寫一篇,以後就可以吃老本了。?
追加字元
借鑑部落格EditText每4位自動新增空格
import android.content.Context;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import com.ifreegroup.ebbly.lib_common.utils.AppLogUtil;
/**
* @Describe:自動新增佔位符的輸入框
* @Date: 2019/06/11
* @Author: dengkewu
* @Contact:
*/
public class PlaceHolderEditText extends android.support.v7.widget.AppCompatEditText {
//上次輸入框中的內容
private String lastString;
//游標的位置
private int selectPosition;
//輸入框內容改變監聽
private TextChangeListener listener;
//追加字元
private String item = "-";
public PlaceHolderEditText(Context context) {
super(context);
initView();
}
public PlaceHolderEditText(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public PlaceHolderEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
/**
* 當輸入框內容改變時的回撥
* @param s 改變後的字串
* @param start 改變之後的游標下標
* @param before 刪除了多少個字元
* @param count 新增了多少個字元
*/
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//因為重新排序之後setText的存在
//會導致輸入框的內容從0開始輸入,這裡是為了避免這種情況產生一系列問題
if (start == 0 && count > 1 && getSelectionStart() == 0) {
return;
}
String textTrim = getText().toString().trim();
if (TextUtils.isEmpty(textTrim)) {
return;
}
//如果 before >0 && count == 0,代表此次操作是刪除操作
if (before > 0 && count == 0) {
selectPosition = start;
if (TextUtils.isEmpty(lastString)) {
return;
}
//將上次的字串去空格 和 改變之後的字串去空格 進行比較
//如果一致,代表本次操作刪除的是空格
if (textTrim.equals(lastString.replaceAll(item, ""))) {
//幫助使用者刪除該刪除的字元,而不是空格
StringBuilder stringBuilder = new StringBuilder(lastString);
stringBuilder.deleteCharAt(start - 1);
selectPosition = start - 1;
setText(stringBuilder.toString());
}
} else {
//此處代表是新增操作
//當游標位於空格之前,新增字元時,需要讓游標跳過空格,再按照之前的邏輯計算游標位置
if ((start + count) % 5 == 0) {
selectPosition = start + count + 1;
} else {
selectPosition = start + count;
}
}
}
@Override
public void afterTextChanged(Editable s) {
//獲取輸入框中的內容,不可以去空格
String etContent = getText().toString();
if (TextUtils.isEmpty(etContent)) {
if (listener != null) {
listener.textChange("");
}
return;
}
//重新拼接字串
String newContent = addSpaceByCredit(etContent);
//儲存本次字串資料
lastString = newContent;
//如果有改變,則重新填充
//防止EditText無限setText()產生死迴圈
if (!newContent.equals(etContent)) {
setText(newContent);
try {
//保證游標的位置
setSelection(selectPosition > newContent.length() ? newContent.length() : selectPosition);
} catch (Exception e) {
//剛好為限制字元的整數倍時新增空格後會出現越界的情況
//AppLogUtil.e("超過限制字元");
}
}
//觸發回撥內容
if (listener != null) {
listener.textChange(newContent);
}
}
});
}
/**
* 輸入框內容回撥,當輸入框內容改變時會觸發
*/
public interface TextChangeListener {
void textChange(String text);
}
public void setTextChangeListener(TextChangeListener listener) {
this.listener = listener;
}
/**
* 每4位新增一個空格
*
* @param content
* @return
*/
public String addSpaceByCredit(String content) {
if (TextUtils.isEmpty(content)) {
return "";
}
content = content.replaceAll(item, "");
if (TextUtils.isEmpty(content)) {
return "";
}
StringBuilder newString = new StringBuilder();
for (int i = 1; i <= content.length(); i++) {
if (i % 4 == 0 && i != content.length()) {
newString.append(content.charAt(i - 1) + item);
} else {
newString.append(content.charAt(i - 1));
}
}
return newString.toString();
}
/**
* 獲取追加字元前輸入內容
* @return
*/
public String getInputText() {
return getText().toString().replaceAll(item, "");
}
}
複製程式碼
核心思路是在文字改變時獲取到原字串取出每一個字元新增上要追加的字元後返回字串並重新setText
。當然中間會有一些坑,比如游標位置、刪除時空格要跳過以及刪除後會再追加空格會造成死迴圈的問題。當然這裡很多情況已經處理過了,如果有其他需求比如手機號碼的111 1111 1111
的形式可以修改addSpaceByCredit
這個方法。
限制字元
借鑑部落格Android EditText限制輸入字元的5種實現方式
et_traveler_content.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) {
String editable = et_traveler_content.getText().toString();
String str = stringFilter(editable.toString());
if (!editable.equals(str)) {
et_traveler_content.setText(str);
//設定新的游標所在位置
et_traveler_content.setSelection(et_traveler_content.getText().toString().length());
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
public String stringFilter(String str) {
// 只允許字母、數字、英文空白字元
String regEx = "[^a-zA-Z0-9\\s]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(str);
return m.replaceAll("");
}
複製程式碼
這裡也是輸入時做過濾然後重新setText
。只要需要對正規表示式熟悉想做什麼限制都可以。
MD效果
系統自帶
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="15dp"
android:padding="0dp"
android:layout_centerVertical="true"
android:gravity="center_vertical">
<EditText
…… />
</android.support.design.widget.TextInputLayout>
複製程式碼
只需要用TextInputLayout
包裹一層便可以實現MD效果。
常用屬性
1.明文、密文
if (isShowPwd) {
// 可視密碼輸入
setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo
.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
} else {
// 非可視密碼狀態
setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
}
複製程式碼
2.預設不獲取焦點
父容器設定(其實只要佈局內有一個控制元件設定就可以)
android:focusableInTouchMode="true"
複製程式碼