開發(fā)小日常
測試:能不能別一打開頁面毛甲,就彈出輸入鍵盤奋单?
博主:好的,我看下,這點(diǎn)疏忽了瘸洛。
測試:能不能別一打開頁面揍移,就顯示光標(biāo)?這個可以不用替用戶決定順序。
博主:好的
測試:你看這個光標(biāo)是不是有點(diǎn)粗反肋?能調(diào)色嗎那伐?
博主:嗯,可以石蔗。
測試:為啥退出這個頁面后喧锦,這個軟鍵盤還在的?
博主:真的嗎抓督?來我看看燃少。
測試:這個可以讓它只輸入數(shù)字嗎?
測試:這個可以讓它只能輸入字母嗎铃在?
測試:這個可以讓它只能輸入字母和數(shù)字嗎阵具?
測試:可以讓用戶只能輸入兩位小數(shù)嗎碍遍?
測試:這個是手機(jī)號,可以限制最多 11 位嗎阳液?
博主:.....
以上是一個真實(shí)的故事怕敬,感受最深刻的就是,就一個編輯框 帘皿,細(xì)節(jié)真的不少东跪,撇去導(dǎo)致這個問題的其他因素,今天的博客是一篇以 EditText 為主題 鹰溜,記錄修改能滿足以上各種需求的干貨文章虽填。
EditText 相關(guān)知識點(diǎn)梳理
首先做個大概的了解,EditText 作為一個編輯框曹动,UI考慮上可分為以下幾點(diǎn):
- 編輯框本身的樣式斋日。例如背景和光標(biāo),字體顏色大小等墓陈。
- 軟鍵盤恶守。例如彈出或關(guān)閉,顯示搜索或確定贡必。
- 輸入限制兔港。例如限制輸入字母或者數(shù)字。
EditText 的樣式
這個在寫 xml 布局文件的階段會涉及到仔拟,Android 系統(tǒng)原生的編輯框押框,丑到開發(fā)者自己都看不下去,雖然在 Material Design 風(fēng)格出來之后好了很多理逊,但是實(shí)際的項(xiàng)目開發(fā)中橡伞,Android 要和 iOS 保持統(tǒng)一,所以還是要自定義晋被。
一般情況下是這種
和這種
以及等等兑徘。
設(shè)置字體顏色大小基本使用,我就不拿出來侮辱大家智商了羡洛,來點(diǎn)實(shí)際的:
EditText 背景
通過設(shè)置 android:background
完成挂脑。
不需要背景,設(shè)置android:background=“@null”
需要特殊的背景話欲侮,就可以通過這個設(shè)置啦崭闲。
EditText 內(nèi)置的小圖標(biāo)
例如上圖左邊的放大鏡小圖標(biāo)那種,可以通過 android:drawableLeft
設(shè)置小圖標(biāo), android:drawablePadding
設(shè)置小圖標(biāo)和輸入字體的間距威蕉。
需要注意的是通過這種方式添加的小圖標(biāo)刁俭,大小是沒法明確設(shè)置的,如果有額外要求韧涨,還是專門使用一個控件吧牍戚。
EditText 的光標(biāo)
EditText 的光標(biāo)顏色默認(rèn)是 colorAccent
的顏色侮繁, colorAccent不僅僅代表到光標(biāo)的顏色,還有項(xiàng)目中很多原生控件激活選擇時的顏色如孝,所以 UI 上如果對編輯框光標(biāo)顏色有需求宪哩,無法直接修改 colorAccent
,或者認(rèn)為光標(biāo)太粗了第晰,想改細(xì)一點(diǎn)锁孟,這類情況都需要設(shè)置光標(biāo)的樣式。
例如設(shè)置光標(biāo)為藍(lán)色茁瘦,寬度為 1 dp,需要先寫一個光標(biāo)樣式文件:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="1dp" />
<solid android:color="#217aff" />
</shape>
然后通過 android:textCursorDrawable
引入這個光標(biāo)樣式品抽。
android:textCursorDrawable="@drawable/edit_cursor_color"
另外:
android:cursorVisible
改變光標(biāo)的可見性
setSelection()
可以改變光標(biāo)的位置。
關(guān)于軟鍵盤
編輯框和軟鍵盤的密不可分腹躁,如果不做任何設(shè)置,頁面上EditText 會默認(rèn)搶占焦點(diǎn)南蓬,彈出軟鍵盤纺非。有的頁面是這個需求,有的則不是赘方,按需選擇烧颖。
EditText 不顯示光標(biāo),只有在點(diǎn)擊時才彈出軟鍵盤
這個在 xml 布局中給 EditText 的父布局設(shè)置觸摸獲取焦點(diǎn)可以解決,設(shè)置屬性是 android:focusableInTouchMode="true"
窄陡。
示例:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:focusableInTouchMode="true" >
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
經(jīng)驗(yàn)證炕淮,這個屬性,并非只有在設(shè)置直接父級布局上有效跳夭,在間接父級布局上也可以涂圆。但是設(shè)置在 ScrollView 父級布局上無效。
軟鍵盤在頁面退出時币叹,不自動關(guān)閉
這個情況是润歉,最近發(fā)現(xiàn)的一個很搞笑的現(xiàn)象。頁面跳轉(zhuǎn)路徑是 A->B 颈抚,B中有 EditText踩衩,在彈出軟鍵盤后,不通過手機(jī)自帶的返回鍵贩汉,而是按 B 頁面中的返回按鈕回到 A 時驱富,發(fā)現(xiàn)軟鍵盤并未隨著B頁面自動關(guān)閉。
原因是 A 頁面設(shè)置了 android:windowSoftInputMode
設(shè)置了 stateHidden
屬性匹舞。
<activity
android:name="xxxxxxx"
android:windowSoftInputMode="adjustPan|stateHidden"/>
解決方法是修改A頁面的 windowSoftInputMode
設(shè)置,一下是一個良心網(wǎng)友@zhangziki自測出來的表格褐鸥,大家自己設(shè)置。
windowSoftInputMode | 鍵盤是否自動收回 |
---|---|
默認(rèn)不指定 | √ |
stateUnspecified | √ |
stateAlwaysVisible | √ |
stateUnchanged | × |
stateHidden | × |
stateAlwaysHidden | × |
stateVisible | × |
stateHidden | × |
stateHidden | × |
設(shè)置軟鍵盤的 enter 鍵
默認(rèn)的鍵盤的 enter 是換行鍵赐稽,如下:
但是有的時候晶疼,產(chǎn)品需求會要求酒贬,鍵盤的 enter 鍵改為其他文字,最常見的是搜索翠霍,點(diǎn)擊搜索后出發(fā)代碼中設(shè)置的搜索邏輯锭吨。
通過在設(shè)置 EditText 的 imeOptions
可以改變 enter 鍵的文案,要著重說明的是,因?yàn)閷⒛J(rèn)的換行鍵換為了搜索鍵寒匙,所以等于放棄了換行功能零如,所以需要通知設(shè)置 android:singleLine="true"
才能使 android:imeOptions="actionSearch"
生效。
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:imeOptions="actionSearch"/>
代碼中可以通過這個方法設(shè)置監(jiān)聽:
mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
switch (actionId) {
case EditorInfo.IME_ACTION_SEARCH://按下搜索鍵
break;
default:
break;
}
return false;
}
});
除了設(shè)置搜索按鈕之外锄弱,還有很多選項(xiàng) actionGo
,actionSend
,actionNext
等考蕾,具體的效果,可以自己試試看会宪,道理都是一樣的肖卧。
EditText 輸入限制
inputType 設(shè)置輸入類型,例如只能輸入數(shù)字,或者輸入密碼
inputType 可以通過 xml 設(shè)置android:inputType
屬性和代碼中 setInputType()
方法設(shè)置掸鹅。
API提供了很多種現(xiàn)成的格式供我們選擇塞帐, 而且這些格式可以自由組合。
android:inputType="numberPassword|number"
以上設(shè)置支持密文輸入巍沙,并且限制只允許輸入數(shù)字
digits 設(shè)置具體的支持字符
這個屬性也支持通過代碼和布局屬性設(shè)置葵姥,和 inputType 的不同之處在于,可以說這個屬性可以自定義具體的支持字符句携。
例如設(shè)置 EditText 只能輸入字符和數(shù)字:
android:digits="0123456789abcdefghijklmnopqrstuvwxyz"
設(shè)置 TextWather 監(jiān)測輸入內(nèi)容
TextWather 監(jiān)聽EditText的字符變化榔幸, 拿到用戶的輸入內(nèi)容,做我們想要的處理矮嫉。這個處理可以分為兩類:
- 作為一個輸入限制使用削咆。實(shí)現(xiàn) inputType 和 digits 兩個屬性無法實(shí)現(xiàn)的特殊的輸入限制需求,例如限制只輸入小數(shù)點(diǎn)后兩位 蠢笋。
- 作為一個事件監(jiān)聽使用态辛。滿足某種情況,觸發(fā)相應(yīng)的邏輯挺尿,例如當(dāng)輸入 16 位身份證號碼時自動調(diào)用接口驗(yàn)證是否真實(shí)有效奏黑。
這里給出一個限制只輸入小數(shù)點(diǎn)后兩位的例子。
//限制的位數(shù)
int digits = 2;
mEditText.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) {
//刪除“.”后面超過2位后的數(shù)據(jù)
if (s.toString().contains(".")) {
if (s.length() - 1 - s.toString().indexOf(".") > digits) {
s = s.toString().subSequence(0,
s.toString().indexOf(".") + digits+1);
editText.setText(s);
editText.setSelection(s.length()); //光標(biāo)移到最后
}
}
//如果"."在起始位置,則起始位置自動補(bǔ)0
if (s.toString().trim().substring(0).equals(".")) {
s = "0" + s;
editText.setText(s);
editText.setSelection(2);
}
//如果起始位置為0,且第二位跟的不是".",則無法后續(xù)輸入
if (s.toString().startsWith("0")
&& s.toString().trim().length() > 1) {
if (!s.toString().substring(1, 2).equals(".")) {
editText.setText(s.subSequence(0, 1));
editText.setSelection(1);
return;
}
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
設(shè)置 InputFilter 過濾器
除了 TextWather编矾,自定義 InputFilter 過濾器也能達(dá)到我們特殊的過濾需求熟史。例如上面的限制兩位小數(shù)點(diǎn)后兩位。
設(shè)置 InputFilter 過濾器需要兩步:
- 自定義 InputFilter, 繼承一個合適的 KeyListener窄俏, 重寫filter()方法蹂匹,這里面編寫你想要的過濾限制。
- EditText 通過
setFilters
設(shè)置過濾器凹蜈。
在我看來限寞,如果實(shí)現(xiàn)輸入限制這類功能忍啸,InputFilter 的實(shí)現(xiàn)方式比 TextWather 顯的更加優(yōu)雅一點(diǎn),而且從方法名稱上看也是理所當(dāng)然的履植。TextWather 更適合去做監(jiān)聽類使用计雌。
下面給出限制兩位小數(shù)點(diǎn)后兩位的 InputFilter 實(shí)現(xiàn)版本。
public class MoneyValueFilter extends DigitsKeyListener {
private static final String TAG = "MoneyValueFilter";
public MoneyValueFilter() {
super(false, true);
}
private int digits = 2;
public MoneyValueFilter setDigits(int d) {
digits = d;
return this;
}
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
CharSequence out = super.filter(source, start, end, dest, dstart, dend);
// if changed, replace the source
if (out != null) {
source = out;
start = 0;
end = out.length();
}
int len = end - start;
// if deleting, source is empty
// and deleting can't break anything
if (len == 0) {
return source;
}
//以點(diǎn)開始的時候玫霎,自動在前面添加0
if(source.toString().equals(".") && dstart == 0){
return "0.";
}
//如果起始位置為0,且第二位跟的不是".",則無法后續(xù)輸入
if(!source.toString().equals(".") && dest.toString().equals("0")){
return "";
}
int dlen = dest.length();
// Find the position of the decimal .
for (int i = 0; i < dstart; i++) {
if (dest.charAt(i) == '.') {
// being here means, that a number has
// been inserted after the dot
// check if the amount of digits is right
return (dlen-(i+1) + len > digits) ?
"" :
new SpannableStringBuilder(source, start, end);
}
}
for (int i = start; i < end; ++i) {
if (source.charAt(i) == '.') {
// being here means, dot has been inserted
// check if the amount of digits is right
if ((dlen-dend) + (end-(i + 1)) > digits)
return "";
else
break; // return new SpannableStringBuilder(source, start, end);
}
}
// if the dot is after the inserted part,
// nothing can break
return new SpannableStringBuilder(source, start, end);
}
}
mEditText.setFilters(new InputFilter[]{new MoneyValueFilter().setDigits(3)});
最后
雖然都是一些小細(xì)節(jié)凿滤,也要重視起來,因?yàn)榧?xì)節(jié)決定成敗吶庶近,希望自己以后要多注意翁脆,努力降低 bug 率,F(xiàn)ighting!
另外鼻种,關(guān)于限制兩位小樹輸入的需求的代碼已上傳我的 github反番,項(xiàng)目地址為 DR_MoneyEditTextDemo,歡迎 Star,熱烈歡迎 Follow 。