Android TextView的TextWatcher使用案例詳解
TextWatcher是一個(gè)文本變化監(jiān)聽接口,定義了三個(gè)接口,分別是beforeTextChanged,onTextChanged,afterTextCahnged. TextWatcher通常與TextView結(jié)合使用,以便在文本變化的不同時(shí)機(jī)做響應(yīng)的處理。TextWatcher中三個(gè)回調(diào)接口都是使用了InputFilter過濾器過濾之后的文字字符作為新的字符對(duì)象。
使用方法
mTextView.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) {
}
@Override
public void afterTextChanged(Editable s) { //屏蔽回車 中英文空格
}
});
我們可以在beforeTextChanged,onTextChanged,afterTextChanged的回調(diào)方法中實(shí)現(xiàn)自己的業(yè)務(wù)邏輯,這三個(gè)參數(shù)代表了TextView文本發(fā)生變化的三個(gè)階段。
beforeTextChanged(CharSequence s, int start, int count, int after)方法是TextView在文本改變之前調(diào)用,并且傳入四個(gè)參數(shù)。 CharSequence s參數(shù)表示當(dāng)前TextView內(nèi)部的mText成員變量,實(shí)際上就是當(dāng)前顯示的文本; int start參數(shù)表示需要改變的文字區(qū)域的起點(diǎn),即選中的文本區(qū)域的起始點(diǎn); int count參數(shù)表示需要改變的文字的字符數(shù)目,即選中的文本區(qū)域的字符的數(shù)目; int after參數(shù)表示替換的文字的字符數(shù)目。 特別的,當(dāng)TextView刪除文本的時(shí)候,after的值為0,此時(shí)TextView使用用空字符串代替需要改變的文字區(qū)域來達(dá)到刪除文字的目的。 圖1.1描述了beforeTextChanged的四個(gè)參數(shù)的含義。
圖1.1 beforeTextChanged的四個(gè)參數(shù)實(shí)例
TextView的setText方法通過調(diào)用sendBeforeTextChanged方法通知所有注冊(cè)的TextWatcher回調(diào)beforeTextChanged方法,此時(shí)傳入的四個(gè)參數(shù),s是當(dāng)前的本地變量mText的值,如果該值為null,即之前沒有給TextView設(shè)置過需要顯示的文本,那么s的值為"";start的值為0;count的值為當(dāng)前mText的長(zhǎng)度;after的值為需要顯示的新文本的長(zhǎng)度。代碼1.1是TextView中setText方法調(diào)用sendBeforeTextChanged的源碼。
代碼1.1 TextView中setText方法調(diào)用sendBeforeTextChanged的源碼
if ( mText != null) {
oldlen = mText.length() ;
sendBeforeTextChanged( mText , 0 , oldlen , text.length()) ;
} else {
sendBeforeTextChanged( "" , 0 , 0 , text.length()) ;
}
onTextChanged(CharSequence s, int start, int before, int count)方法是TextView在文本改變的時(shí)候調(diào)用,此時(shí)mText成員變量已經(jīng)被修改為新的文本,并且傳入四個(gè)參數(shù)。 CharSequence s參數(shù)表示當(dāng)前TextView內(nèi)部的mText成員變量,此時(shí)的mText已經(jīng)被修改過了,但此時(shí)mText所表示的文本還沒有被顯示到UI組件上; int start參數(shù)表示改變的文字區(qū)域的起點(diǎn); int before參數(shù)表示改變的文字區(qū)域在改變前的舊的文本長(zhǎng)度,即選中文字區(qū)域的文本長(zhǎng)度; int after參數(shù)表示改變的文字區(qū)域在修改后的新的文本長(zhǎng)度。 特別的,當(dāng)TextView添加文本的時(shí)候,before 的值為0,此時(shí)相當(dāng)于TextView將空的字符區(qū)域用新的文本代替。
afterTextChanged(Editable s)方法是TextView在調(diào)用完所有已注冊(cè)的TextWatcher的onTextChanged方法之后回調(diào)的。此時(shí)mText成員變量已經(jīng)被修改為新的文本,并且傳入s,該參數(shù)s實(shí)際上就是mText。通過該接口,咱們可以再次修改將要展示的文字。
圖1.2描述了這三個(gè)方法在TextView文字變化時(shí)的調(diào)用流程。
圖1.2 TextView文字變化時(shí)的調(diào)用流程
afterTextChanged的參數(shù)類型是Editable,這是一個(gè)可編輯的對(duì)象,該對(duì)像就Textview的內(nèi)部變量mText,此時(shí)的mText是·可編輯的,實(shí)際上是一個(gè)SpannableStringBuilder對(duì)象。代碼1.1是TextView的setText方法中text的轉(zhuǎn)換邏輯。從代碼中可以看出,當(dāng)type == BufferType.EDITABLE || getKeyListener() != null || needEditableForNotification為true的時(shí)候,mEditableFactory會(huì)調(diào)用newEditable的方法創(chuàng)建一個(gè)可編輯的對(duì)象SpannableStringBuilder。
代碼 1.1 TextView的setText方法中text的轉(zhuǎn)換邏輯
boolean needEditableForNotification = false;
if ( mListeners != null && mListeners.size() != 0) {
needEditableForNotification = true;
}
if (type == BufferType. EDITABLE || getKeyListener() != null ||
needEditableForNotification) {
createEditorIfNeeded() ;
Editable t = mEditableFactory.newEditable(text) ;
text = t ;
setFilters(t , mFilters) ;
InputMethodManager imm = InputMethodManager.peekInstance() ;
if (imm != null) imm.restartInput( this) ;
} else if (type == BufferType. SPANNABLE || mMovement != null) {
text = mSpannableFactory.newSpannable(text) ;
} else if (!(text instanceof CharWrapper)) {
text = TextUtils. stringOrSpannedString(text) ;
}
代碼1.2是mEditableFactory的newEditable方法,該方法是一個(gè)工廠方法,創(chuàng)建一個(gè) SpannableStringBuilder對(duì)象。
代碼 1.2 mEditableFactory的newEditable方法
public Editable newEditable(CharSequence source) {
return new SpannableStringBuilder(source) ;
}
SpannableStringBuilder實(shí)現(xiàn)了CharSequence, GetChars, Spannable, Editable,Appendable, GraphicsOperations的接口,內(nèi)部的string是可以變化的。不過咱們修改afterTextChanged 中的參數(shù)s的時(shí)候, 需要注意循環(huán)調(diào)用的潛在風(fēng)險(xiǎn),因?yàn)镾pannableStringBuilder會(huì)在自己內(nèi)部保存TextView的mChangeWatcher對(duì)象,代碼1.3描述了設(shè)置過程。如代碼所示,text通過setSpan方法添加了mChangeWatcher對(duì)象,以監(jiān)聽整個(gè)text的變化,并且做回調(diào)。當(dāng)text的內(nèi)容發(fā)生變化的時(shí)候,會(huì)通過span機(jī)制調(diào)用mChangeWatcher中的相應(yīng)方法。
代碼1.3 TextView的setText方法給mText添加mChangeWatcher的源碼
Spannable sp = (Spannable) text ;
// Remove any ChangeWatchers that might have come from other TextViews.
final ChangeWatcher[] watchers = sp.getSpans( 0 , sp.length() , ChangeWatcher. class) ;
final int count = watchers. length ;
for ( int i = 0 ; i < count ; i++) {
sp.removeSpan(watchers[i]) ;
}
if ( mChangeWatcher == null) mChangeWatcher = new ChangeWatcher() ;
sp.setSpan( mChangeWatcher , 0 , textLength , Spanned. SPAN_INCLUSIVE_INCLUSIVE |
( CHANGE_WATCHER_PRIORITY << Spanned. SPAN_PRIORITY_SHIFT)) ;
mChangeWatcher是一個(gè)ChangeWatcher對(duì)象,ChangeWatcher是TextView的內(nèi)部類,實(shí)現(xiàn)了TextWatcher, SpanWatcher接口偶,代碼1.4描述了ChangeWatcher實(shí)現(xiàn)的TextWatcher的三個(gè)回調(diào)接口。這三個(gè)接口會(huì)分別調(diào)用TextView的相應(yīng)的方法,通知所有注冊(cè)的TextWatcher的調(diào)用相應(yīng)的三個(gè)回調(diào)接口。其中,TextView的handleTextChanged還會(huì)調(diào)用invalidate()和checkForResize()方法重繪UI界面.
代碼1.4 ChangeWatcher實(shí)現(xiàn)的TextWatcher的三個(gè)回調(diào)接口
public void beforeTextChanged(CharSequence buffer , int start ,
int before , int after) {
.......
TextView. this.sendBeforeTextChanged(buffer , start , before , after) ;
}
public void onTextChanged(CharSequence buffer , int start , int before , int after) {
.......
TextView. this.handleTextChanged(buffer , start , before , after) ;
......
}
public void afterTextChanged(Editable buffer) {
......
TextView. this.sendAfterTextChanged(buffer) ;
......
}
通過SpannableStringBuilder,ChangeWatcher這兩個(gè)類再加上span機(jī)制,android可以在文本改變的時(shí)候通知所有注冊(cè)的TextWatcher方法調(diào)用相應(yīng)的三個(gè)接口。但是這又會(huì)使咱們?cè)赥extWatcher的afterTextChanged中修改參數(shù)s的時(shí)候,再次調(diào)用TextWatcher的三個(gè)回調(diào)接口,這樣如果afterTextChanged不能因?yàn)槟承l件的判斷,終止對(duì)s的修改,那么就會(huì)形成無限循環(huán)調(diào)用。 類似TextView的setText的方法也會(huì)在相應(yīng)的時(shí)機(jī)通知所有注冊(cè)的TextWatcher調(diào)用響應(yīng)的三個(gè)接口,如果咱們?cè)赥extWatcher的三個(gè)接口中調(diào)用TextView的setText方法也會(huì)導(dǎo)致無限循環(huán)調(diào)用。圖1.3描述了無限調(diào)用的示例。
圖1.3 無限調(diào)用示例
但是有時(shí)候我們可能 需要根據(jù)業(yè)務(wù)需求更改顯示的文本,比如過濾不必要的字符,過濾非法的文字,添加必要的結(jié)束字符等。這個(gè)時(shí)候我們有時(shí)候會(huì)給TextView添加一個(gè)TextWatcher,然后在某個(gè)接口回調(diào)中根據(jù)傳入的參數(shù)修改將要顯示的文本,這可以滿足咱們的業(yè)務(wù)需求,不過有可能會(huì)導(dǎo)致無限循環(huán)調(diào)用。針對(duì)這個(gè)問題,咱們可以 通過InputFilter來完成修改文本的目的。 InputFilter是一個(gè)接口,內(nèi)部定義了filter方法,這個(gè)方法的作用是修改傳入的字符串,如果返回值為null,那么保持原來的字符串。代碼1.5是這個(gè)方法的定義。
代碼1.5 InputFilter內(nèi)部定義了filter接口
public CharSequence filter(CharSequence source , int start , int end ,
Spanned dest , int dstart , int dend) ;
如代碼所示,filter方法需要傳入六個(gè)參數(shù),其中
source參數(shù)是即將替換選中字符區(qū)域的字符串對(duì)象;
start參數(shù)表示source的起始位置;
end參數(shù)表示source的終止位置。通過source,start,end這三個(gè)參數(shù)可以描述出替換選中字符區(qū)域的新的字符串。
dest表示選中文字區(qū)域的文本對(duì)象,TextView的setText方法調(diào)用filter方法時(shí),傳入的dest為
EMPTY_SPANNED,修改文字時(shí),傳入的
dest為TextView中保存的mText;
dstart表示選中的文字區(qū)域的起始位置;
dend表示選中的文字區(qū)域的終止位置。
該方法的返回值是用來替換source作為新的替換文本。
圖1.4是有一個(gè)filter實(shí)例,描述了filter的六個(gè)參數(shù)的含義。
圖1.4 filter實(shí)例
InputFilter接口內(nèi)部提供了兩個(gè)子類,分別是AllCaps和LengthFilter。
AllCaps是將文本中的小寫字符全部轉(zhuǎn)為大寫字符的過濾器,通過該過濾器,TextView能將輸入文本中的小寫字符轉(zhuǎn)為大寫字符,然后顯示出來;
LengthFilter是刪除掉超過長(zhǎng)度maxLength的字符的過濾器。在xml中配置了maxLength之后,TextView在創(chuàng)建實(shí)例的時(shí)候,會(huì)生成一個(gè)LengthFilter的過濾器,以便達(dá)到限定顯示字符長(zhǎng)度的功能。
咱們自己也可以定義滿足咱們業(yè)務(wù)需求的inputfilter,以達(dá)到在TextWatcher接口回調(diào)之前過濾掉無用或者非法字符的功能,代碼1.6是一個(gè)InputFilter的實(shí)現(xiàn)子類,該類過濾掉無用的空白符。
代碼1.6 過濾掉無用空白符的過濾器
static class NoUsageCharInputFilter implements InputFilter {
@Override
public CharSequence filter(CharSequence source , int start , int end , Spanned dest , int dstart , int dend) {
return source == null ? null : source.toString().replaceAll( " \\ s" , "") ;
}
}
定義完InputFilter的實(shí)現(xiàn)子類之后,咱們就可以將實(shí)現(xiàn)了的過濾器添加到TextView的過濾器數(shù)組中,代碼1.7是一個(gè)添加過濾器的示例,如代碼所示,通過source.setFilters(inputFilters)方法可以給TextView設(shè)置InputFilter數(shù)組,由于我們的功能是添加過濾器,因此 需要將source原本的過濾器數(shù)組中的元素添加到新的過濾器數(shù)組中,否則source原本的過濾器數(shù)組會(huì)被覆蓋掉,那樣即使咱們?cè)趚ml文件中配置了maxLength,source也不會(huì)使用LengthInputFilter來限定文本的長(zhǎng)度。
代碼1.7 添加過濾器的示例
public static void addNoUsageCharInputFilter(TextView source) {
if (source == null)
return;
InputFilter[] inputFilters = new InputFilter[source.getFilters() != null ? source.getFilters(). length + 1: 1] ;
inputFilters[ 0] = new NoUsageCharInputFilter() ;
if (source.getFilters() != null) {
for ( int i = 0 ; i < source.getFilters(). length ; i++)
inputFilters[i + 1] = source.getFilters()[i] ;
}
source.setFilters(inputFilters) ;
}
TexView在設(shè)置完過濾器數(shù)組之后,它的setText方法會(huì)在調(diào)用sendBeforeTextChanged之前先用過濾器數(shù)組中的過濾器修改傳入的文本參數(shù),setText方法調(diào)用過濾器的實(shí)現(xiàn)見代碼1.8。
代碼1.8 TexView中setText調(diào)用過濾器的實(shí)現(xiàn)代碼
int n = mFilters.length;
for (int i = 0; i < n; i++) {
CharSequence out = mFilters[i].filter(text, 0, text.length(), EMPTY_SPANNED, 0, 0);
if (out != null) {
text = out;
}
}
if (notifyBefore) {
if (mText != null) {
oldlen = mText.length();
sendBeforeTextChanged(mText, 0, oldlen, text.length());
} else {
sendBeforeTextChanged("", 0, 0, text.length());
}
}
通過以上的分析:
1.咱們可以使用TextWatcher監(jiān)聽TextView文本變化的三個(gè)時(shí)機(jī),并在回調(diào)函數(shù)中做相應(yīng)的處理;
2.但是在回調(diào)函數(shù)中處理業(yè)務(wù)的時(shí)候,需要注意不要調(diào)用該TextView的setText方法,否則會(huì)發(fā)生無限循環(huán)調(diào)用;
3.在TextWatcher 的回調(diào)接口afterTextChanged方法中修改參數(shù)s的時(shí)候,也要注意在修改了s之后,會(huì)觸發(fā)文本變化,導(dǎo)致TextView中所有注冊(cè)的TextWatcher再次回調(diào)自己的的三個(gè)回調(diào)函數(shù),此時(shí)需要預(yù)防無限循環(huán)調(diào)用的發(fā)生。
4.如果需要修改傳入文本,那么可以實(shí)現(xiàn)InputFilter接口,然后給TextView添加符合業(yè)務(wù)需求的過濾器;
5.給TextView添加自定義的過濾器的時(shí)候,需要注意使用setFilter方法設(shè)置過濾器會(huì)覆蓋掉TextView原本的過濾器,如果不想舍棄TextView原本的過濾器,那么需要將原本的過濾器添加到新的過濾器數(shù)組中。
6.TextView使用InputFilter過濾器數(shù)組的時(shí)候,是從第一個(gè)過濾器到最后一個(gè)過濾器依次使用的,因此咱們給TextView設(shè)置過濾器數(shù)組的時(shí)候需要考慮過濾器的順序。
到此這篇關(guān)于Android TextView的TextWatcher使用案例詳解的文章就介紹到這了,更多相關(guān)Android TextView的TextWatcher使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
android實(shí)現(xiàn)一個(gè)圖片驗(yàn)證碼倒計(jì)時(shí)功能
本文通過實(shí)例代碼給大家介紹了android實(shí)現(xiàn)一個(gè)圖片驗(yàn)證碼倒計(jì)時(shí)功能,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-11-11
Ubuntu中為Android系統(tǒng)實(shí)現(xiàn)內(nèi)置Java應(yīng)用程序測(cè)試Application Frameworks層的硬件服務(wù)
本文主要介紹Ubuntu中為Android系統(tǒng)內(nèi)置應(yīng)用訪問Application Frameworks層的硬件服務(wù),這里提供了詳細(xì)的流程和代碼實(shí)例,有興趣的朋友可以參考下2016-08-08
Android 跨進(jìn)程通Messenger(簡(jiǎn)單易懂)
這篇文章主要介紹了Android Messenger跨進(jìn)程通的相關(guān)資料,非常簡(jiǎn)單容易理解,對(duì)android messenger 進(jìn)程通訊的相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-08-08
Android實(shí)現(xiàn)將應(yīng)用崩潰信息發(fā)送給開發(fā)者并重啟應(yīng)用的方法
這篇文章主要介紹了Android實(shí)現(xiàn)將應(yīng)用崩潰信息發(fā)送給開發(fā)者并重啟應(yīng)用的方法,涉及Android錯(cuò)誤處理與應(yīng)用操作的相關(guān)技巧,需要的朋友可以參考下2016-03-03
Android條紋進(jìn)度條的實(shí)現(xiàn)(調(diào)整view寬度仿進(jìn)度條)
這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)條紋進(jìn)度條的方法,主要是通過調(diào)整view寬度仿進(jìn)度條,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧2018-09-09
Android項(xiàng)目中使用HTTPS配置的步驟詳解
這篇文章主要給大家介紹了關(guān)于Android項(xiàng)目中使用HTTPS配置步驟的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-06-06
詳解Android如何實(shí)現(xiàn)好的彈層體驗(yàn)效果
當(dāng)前?App?的設(shè)計(jì)趨勢(shì)越來越希望給用戶沉浸式體驗(yàn),這種設(shè)計(jì)會(huì)讓用戶盡量停留在當(dāng)前的界面,而不需要太多的跳轉(zhuǎn),這就需要引入彈層。本篇我們就來講講彈層這塊需要注意哪些用戶體驗(yàn)2022-11-11

