FunEditText
最近需要做一個(gè)比較有趣的輸入框效果,用原生的EditText不好實(shí)現(xiàn),效果是這樣的。

這是一個(gè)數(shù)字的輸入框,這種樣式一般用于類似驗(yàn)證碼之類的輸入。我們看到底下的線是跟著每個(gè)輸入的數(shù)字而不是一條完整的線。
這里我把每一個(gè)數(shù)字跟底部的線當(dāng)成一個(gè)整體,也就是每個(gè)這樣的整體,它是由一個(gè)TextView + 底部橫線組成的。同時(shí)這個(gè)是個(gè)輸入框,我們需要吊起軟鍵盤并捕捉到用戶輸入什么,再賦值給我們的TextView顯示出來。
用一個(gè)比較取巧的辦法,就是我們定義一個(gè)寬高都為0的EditText,用來捕捉輸入事件,而EditText監(jiān)聽輸入字符,我們很容易聯(lián)想到添加一個(gè)TextWatcher來監(jiān)聽EditText的字符輸入,但是TextWatcher有一個(gè)問題,它監(jiān)聽的是鍵盤的輸入,而不是鍵盤的點(diǎn)擊(當(dāng)然你可以根據(jù)輸入情況來判斷是點(diǎn)擊的什么按鍵,這里主要是刪除鍵的判斷)。
InputConnection 和 InputConnectionWrapper
我們知道EditText本身是可以監(jiān)聽鍵盤輸入的,我們?cè)贓ditText里面搜索一下有關(guān)輸入Input的相關(guān)信息,最后可以發(fā)現(xiàn)在View下有這么一個(gè)方法
* Create a new InputConnection for an InputMethod to interact
* with the view. The default implementation returns null, since it doesn't
* support input methods. You can override this to implement such support.
* This is only needed for views that take focus and text input.
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return null;
}
截取了一部分的注釋,可以看到,這個(gè)方法實(shí)在view需要捕捉焦點(diǎn)并且獲取文本輸入的時(shí)候才重寫并且返回一個(gè)InputConnection的。那這個(gè)InputConnection應(yīng)該就是我們要找的能處理鍵盤點(diǎn)擊跟文本輸入的類了。
InputConnection是一個(gè)接口類,是鍵盤輸入事件跟應(yīng)用程序溝通的渠道,可以處理光標(biāo),文本輸入,已經(jīng)按鍵點(diǎn)擊等事件
InputConnectionWrapper 是InputConnection的代理類,借助InputConnectionWrapper,我們可以重寫InputConnection的一些方法,來做一些有趣的事情。
ok,現(xiàn)在我們新建一個(gè)類,繼承InputConnectionWrapper,這里主要重寫了三個(gè)方法,
- sendKeyEvent(KeyEvent event),我需要捕捉刪除按鈕點(diǎn)擊,
- commitText(CharSequence text, int newCursorPosition)我需要捕捉文本的輸入,
- deleteSurroundingText(int beforeLength, int afterLength)我查資料的時(shí)候聽說有的輸入法在點(diǎn)擊刪除按鈕時(shí)不會(huì)回調(diào)sendKeyEvent()但是會(huì)回調(diào)這個(gè)方法,我需要在這里手動(dòng)調(diào)用一次sendKeyEvent(),姑且信他一波
所以整個(gè)代碼是這樣的
public class FunInputConnection extends InputConnectionWrapper {
String[] digits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
/**
* Initializes a wrapper.
*
* <p><b>Caveat:</b> Although the system can accept {@code (InputConnection) null} in some
* places, you cannot emulate such a behavior by non-null {@link InputConnectionWrapper} that
* has {@code null} in {@code target}.</p>
*
* @param target the {@link InputConnection} to be proxied.
* @param mutable set {@code true} to protect this object from being reconfigured to target
* another {@link InputConnection}. Note that this is ignored while the target is {@code null}.
*/
public FunInputConnection(InputConnection target, boolean mutable) {
super(target, mutable);
}
@Override
public boolean sendKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
if (null != listener) {
listener.onDelete();
return false;
}
}
return super.sendKeyEvent(event);
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
if (null != listener && Arrays.asList(digits).contains(text.toString())) {
listener.onTextInput(text);
}
return super.commitText(text, newCursorPosition);
}
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (beforeLength == 1 && afterLength == 0) {
return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
}
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
}
因?yàn)槭侵荒茌斎霐?shù)字,所以我在commitText時(shí)過濾了其他的輸入文本,
再回到我們的EditText里,重寫我們的onCreateInputConnection方法,把我們重寫的FunInputConnection傳進(jìn)去
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new FunInputConnection(super.onCreateInputConnection(outAttrs), true);
}
至此,攔截鍵盤點(diǎn)擊跟輸入,已經(jīng)只能輸入數(shù)字的處理已經(jīng)完成了。
加個(gè)背景色,看看最后的效果
