欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android TextView的TextWatcher使用案例詳解

 更新時間:2021年09月01日 09:24:46   作者:LuckySort  
這篇文章主要介紹了Android TextView的TextWatcher使用案例詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下

TextWatcher是一個文本變化監(jiān)聽接口,定義了三個接口,分別是beforeTextChanged,onTextChanged,afterTextCahnged. TextWatcher通常與TextView結合使用,以便在文本變化的不同時機做響應的處理。TextWatcher中三個回調接口都是使用了InputFilter過濾器過濾之后的文字字符作為新的字符對象。

使用方法

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的回調方法中實現自己的業(yè)務邏輯,這三個參數代表了TextView文本發(fā)生變化的三個階段。

beforeTextChanged(CharSequence s, int start, int count, int after)方法是TextView在文本改變之前調用,并且傳入四個參數。 CharSequence s參數表示當前TextView內部的mText成員變量,實際上就是當前顯示的文本; int start參數表示需要改變的文字區(qū)域的起點,即選中的文本區(qū)域的起始點; int count參數表示需要改變的文字的字符數目,即選中的文本區(qū)域的字符的數目; int after參數表示替換的文字的字符數目。 特別的,當TextView刪除文本的時候,after的值為0,此時TextView使用用空字符串代替需要改變的文字區(qū)域來達到刪除文字的目的。 圖1.1描述了beforeTextChanged的四個參數的含義。

圖1.1 beforeTextChanged的四個參數實例

TextView的setText方法通過調用sendBeforeTextChanged方法通知所有注冊的TextWatcher回調beforeTextChanged方法,此時傳入的四個參數,s是當前的本地變量mText的值,如果該值為null,即之前沒有給TextView設置過需要顯示的文本,那么s的值為"";start的值為0;count的值為當前mText的長度;after的值為需要顯示的新文本的長度。代碼1.1是TextView中setText方法調用sendBeforeTextChanged的源碼。

代碼1.1 TextView中setText方法調用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在文本改變的時候調用,此時mText成員變量已經被修改為新的文本,并且傳入四個參數。 CharSequence s參數表示當前TextView內部的mText成員變量,此時的mText已經被修改過了,但此時mText所表示的文本還沒有被顯示到UI組件上; int start參數表示改變的文字區(qū)域的起點; int before參數表示改變的文字區(qū)域在改變前的舊的文本長度,即選中文字區(qū)域的文本長度; int after參數表示改變的文字區(qū)域在修改后的新的文本長度。 特別的,當TextView添加文本的時候,before 的值為0,此時相當于TextView將空的字符區(qū)域用新的文本代替。

afterTextChanged(Editable s)方法是TextView在調用完所有已注冊的TextWatcher的onTextChanged方法之后回調的。此時mText成員變量已經被修改為新的文本,并且傳入s,該參數s實際上就是mText。通過該接口,咱們可以再次修改將要展示的文字。

圖1.2描述了這三個方法在TextView文字變化時的調用流程。

圖1.2  TextView文字變化時的調用流程

afterTextChanged的參數類型是Editable,這是一個可編輯的對象,該對像就Textview的內部變量mText,此時的mText是·可編輯的,實際上是一個SpannableStringBuilder對象。代碼1.1是TextView的setText方法中text的轉換邏輯。從代碼中可以看出,當type == BufferType.EDITABLE || getKeyListener() != null || needEditableForNotification為true的時候,mEditableFactory會調用newEditable的方法創(chuàng)建一個可編輯的對象SpannableStringBuilder。

代碼  1.1  TextView的setText方法中text的轉換邏輯

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方法,該方法是一個工廠方法,創(chuàng)建一個 SpannableStringBuilder對象。

代碼  1.2  mEditableFactory的newEditable方法

public Editable  newEditable(CharSequence source) {
    return new SpannableStringBuilder(source) ;
}

SpannableStringBuilder實現了CharSequence, GetChars, Spannable, Editable,Appendable, GraphicsOperations的接口,內部的string是可以變化的。不過咱們修改afterTextChanged 中的參數s的時候, 需要注意循環(huán)調用的潛在風險,因為SpannableStringBuilder會在自己內部保存TextView的mChangeWatcher對象,代碼1.3描述了設置過程。如代碼所示,text通過setSpan方法添加了mChangeWatcher對象,以監(jiān)聽整個text的變化,并且做回調。當text的內容發(fā)生變化的時候,會通過span機制調用mChangeWatcher中的相應方法。

代碼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是一個ChangeWatcher對象,ChangeWatcher是TextView的內部類,實現了TextWatcher, SpanWatcher接口偶,代碼1.4描述了ChangeWatcher實現的TextWatcher的三個回調接口。這三個接口會分別調用TextView的相應的方法,通知所有注冊的TextWatcher的調用相應的三個回調接口。其中,TextView的handleTextChanged還會調用invalidate()和checkForResize()方法重繪UI界面.

代碼1.4 ChangeWatcher實現的TextWatcher的三個回調接口

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這兩個類再加上span機制,android可以在文本改變的時候通知所有注冊的TextWatcher方法調用相應的三個接口。但是這又會使咱們在TextWatcher的afterTextChanged中修改參數s的時候,再次調用TextWatcher的三個回調接口,這樣如果afterTextChanged不能因為某些條件的判斷,終止對s的修改,那么就會形成無限循環(huán)調用。 類似TextView的setText的方法也會在相應的時機通知所有注冊的TextWatcher調用響應的三個接口,如果咱們在TextWatcher的三個接口中調用TextView的setText方法也會導致無限循環(huán)調用。圖1.3描述了無限調用的示例。

圖1.3 無限調用示例

但是有時候我們可能 需要根據業(yè)務需求更改顯示的文本,比如過濾不必要的字符,過濾非法的文字,添加必要的結束字符等。這個時候我們有時候會給TextView添加一個TextWatcher,然后在某個接口回調中根據傳入的參數修改將要顯示的文本,這可以滿足咱們的業(yè)務需求,不過有可能會導致無限循環(huán)調用。針對這個問題,咱們可以 通過InputFilter來完成修改文本的目的。 InputFilter是一個接口,內部定義了filter方法,這個方法的作用是修改傳入的字符串,如果返回值為null,那么保持原來的字符串。代碼1.5是這個方法的定義。

代碼1.5 InputFilter內部定義了filter接口

public CharSequence  filter(CharSequence source , int start , int end ,
                           Spanned dest , int dstart , int dend) ;

如代碼所示,filter方法需要傳入六個參數,其中

source參數是即將替換選中字符區(qū)域的字符串對象;

start參數表示source的起始位置;

end參數表示source的終止位置。通過source,start,end這三個參數可以描述出替換選中字符區(qū)域的新的字符串。

dest表示選中文字區(qū)域的文本對象,TextView的setText方法調用filter方法時,傳入的dest為

EMPTY_SPANNED,修改文字時,傳入的

       dest為TextView中保存的mText;

dstart表示選中的文字區(qū)域的起始位置;

dend表示選中的文字區(qū)域的終止位置。

該方法的返回值是用來替換source作為新的替換文本。

圖1.4是有一個filter實例,描述了filter的六個參數的含義。

圖1.4 filter實例

InputFilter接口內部提供了兩個子類,分別是AllCaps和LengthFilter。

AllCaps是將文本中的小寫字符全部轉為大寫字符的過濾器,通過該過濾器,TextView能將輸入文本中的小寫字符轉為大寫字符,然后顯示出來;

LengthFilter是刪除掉超過長度maxLength的字符的過濾器。在xml中配置了maxLength之后,TextView在創(chuàng)建實例的時候,會生成一個LengthFilter的過濾器,以便達到限定顯示字符長度的功能。

咱們自己也可以定義滿足咱們業(yè)務需求的inputfilter,以達到在TextWatcher接口回調之前過濾掉無用或者非法字符的功能,代碼1.6是一個InputFilter的實現子類,該類過濾掉無用的空白符。

代碼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的實現子類之后,咱們就可以將實現了的過濾器添加到TextView的過濾器數組中,代碼1.7是一個添加過濾器的示例,如代碼所示,通過source.setFilters(inputFilters)方法可以給TextView設置InputFilter數組,由于我們的功能是添加過濾器,因此 需要將source原本的過濾器數組中的元素添加到新的過濾器數組中,否則source原本的過濾器數組會被覆蓋掉,那樣即使咱們在xml文件中配置了maxLength,source也不會使用LengthInputFilter來限定文本的長度。

代碼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在設置完過濾器數組之后,它的setText方法會在調用sendBeforeTextChanged之前先用過濾器數組中的過濾器修改傳入的文本參數,setText方法調用過濾器的實現見代碼1.8。

代碼1.8 TexView中setText調用過濾器的實現代碼

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文本變化的三個時機,并在回調函數中做相應的處理;

2.但是在回調函數中處理業(yè)務的時候,需要注意不要調用該TextView的setText方法,否則會發(fā)生無限循環(huán)調用;

3.在TextWatcher 的回調接口afterTextChanged方法中修改參數s的時候,也要注意在修改了s之后,會觸發(fā)文本變化,導致TextView中所有注冊的TextWatcher再次回調自己的的三個回調函數,此時需要預防無限循環(huán)調用的發(fā)生。

4.如果需要修改傳入文本,那么可以實現InputFilter接口,然后給TextView添加符合業(yè)務需求的過濾器;

5.給TextView添加自定義的過濾器的時候,需要注意使用setFilter方法設置過濾器會覆蓋掉TextView原本的過濾器,如果不想舍棄TextView原本的過濾器,那么需要將原本的過濾器添加到新的過濾器數組中。

6.TextView使用InputFilter過濾器數組的時候,是從第一個過濾器到最后一個過濾器依次使用的,因此咱們給TextView設置過濾器數組的時候需要考慮過濾器的順序。

到此這篇關于Android TextView的TextWatcher使用案例詳解的文章就介紹到這了,更多相關Android TextView的TextWatcher使用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論