解决方案

android的TextView的TextWatcher使用

seo靠我 2023-09-24 03:25:25

TextWatcher是一个文本变化监听接口,定义了三个接口,分别是beforeTextChanged,onTextChanged,afterTextCahnged. TexSEO靠我tWatcher通常与TextView结合使用,以便在文本变化的不同时机做响应的处理。TextWatcher中三个回调接口都是使用了InputFilter过滤器过滤之后的文字字符作为新的字符对象。 SEO靠我 使用方法

mTextView.addTextChangedListener(new TextWatcher(){ @Override

public voiSEO靠我d beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override

public void onTextChSEO靠我anged(CharSequence s, int start, int before, int count) {

}

        @Override

public void afterTextChanged(EditSEO靠我able s) { //屏蔽回车 中英文空格

} });

我们可以在beforeTextChanged,onTextChanged,afterTextChanged的回调方法中实SEO靠我现自己的业务逻辑,这三个参数代表了TextView文本发生变化的三个阶段。

beforeTextChanged(CharSequence s, int start, int count, int aftSEO靠我er)

方法是TextView在文本改变之前调用,并且传入四个参数。 CharSequence s参数表示当前TextView内部的mText成员变量,实际上就是当前显示的文本SEO靠我; int start参数表示需要改变的文字区域的起点,即选中的文本区域的起始点; int count参数表示需要改变的文字的字符数目,即选中的文本SEO靠我区域的字符的数目; int after参数表示替换的文字的字符数目。 特别的,当TextView删除文本的时候,after的值为0,此时TextViSEO靠我ew使用用空字符串代替需要改变的文字区域来达到删除文字的目的。 图1.1描述了beforeTextChanged的四个参数的含义。 图1.1 befSEO靠我oreTextChanged的四个参数实例

TextView的setText方法通过调用sendBeforeTextChanged方法通知所有注册的TextWatcher回调beforeTextChanSEO靠我ged方法,此时传入的四个参数,s是当前的本地变量mText的值,如果该值为null,即之前没有给TextView设置过需要显示的文本,那么s的值为"";start的值为0;count的值为当前mTeSEO靠我xt的长度;after的值为需要显示的新文本的长度。代码1.1是TextView中setText方法调用sendBeforeTextChanged的源码。 代码1.1 TexSEO靠我tView中setText方法调用sendBeforeTextChanged的源码 if ( mText !=  null) {

oldlen =  mText.length(SEO靠我) ;

    sendBeforeTextChanged( mText ,  0 , oldlen , text.length()) ;

}  else {

sendBeforeTextChanged( "" ,SEO靠我  0 ,  0 , text.length()) ;

}

onTextChanged(CharSequence s, int start, int before, int count)

方法是TextViSEO靠我ew在文本改变的时候调用,此时mText成员变量已经被修改为新的文本,并且传入四个参数。 CharSequence s参数表示当前TextView内部的mText成员变量,此SEO靠我时的mText已经被修改过了,但此时mText所表示的文本还没有被显示到UI组件上; int start参数表示改变的文字区域的起点; int beSEO靠我fore参数表示改变的文字区域在改变前的旧的文本长度,即选中文字区域的文本长度; int after参数表示改变的文字区域在修改后的新的文本长度。 SEO靠我特别的,当TextView添加文本的时候,before 的值为0,此时相当于TextView将空的字符区域用新的文本代替。

afterTextChanged(Editable s)

方法是TextViewSEO靠我在调用完所有已注册的TextWatcher的onTextChanged方法之后回调的。此时mText成员变量已经被修改为新的文本,并且传入s,该参数s实际上就是mText。通过该接口,咱们可以再次修改SEO靠我将要展示的文字。 图1.2描述了这三个方法在TextView文字变化时的调用流程。 图1.2  TextView文字变化时的调用流程

afterTexSEO靠我tChanged的参数类型是Editable,这是一个可编辑的对象,该对像就Textview的内部变量mText,此时的mText是·可编辑的,实际上是一个SpannableStringBuilderSEO靠我对象。代码1.1是TextView的setText方法中text的转换逻辑。从代码中可以看出,当type == BufferType.EDITABLE || getKeyListener() != nSEO靠我ull || needEditableForNotification为true的时候,mEditableFactory会调用newEditable的方法创建一个可编辑的对象SpannableStrinSEO靠我gBuilder。 代码  1.1  TextView的setText方法中text的转换逻辑 boolean needEditableForNotSEO靠我ification =  false;

if ( mListeners !=  null &&  mListeners.size() !=  0) {

needEditableForNotificatioSEO靠我n =  true;

}

if (type == BufferType. EDITABLE || getKeyListener() !=  null ||

needEditableForNotificatiSEO靠我on) {

    createEditorIfNeeded() ;

    Editable t =  mEditableFactory.newEditable(text) ;

    text = t ;

setFilters(SEO靠我t ,  mFilters) ;

    InputMethodManager imm = InputMethodManager.peekInstance() ;

if (imm !=  null) imm.reSEO靠我startInput( this) ;

}  else if (type == BufferType. SPANNABLE ||  mMovement !=  null) {

text =  mSpannSEO靠我ableFactory.newSpannable(text) ;

}  else if (!(text  instanceof CharWrapper)) {

text = TextUtils. striSEO靠我ngOrSpannedString(text) ;

}

代码1.2是mEditableFactory的newEditable方法,该方法是一个工厂方法,创建一个 SpannableStringBuildeSEO靠我r对象。 代码  1.2  mEditableFactory的newEditable方法 public Editable  newEditable(SEO靠我CharSequence source) {

    return new SpannableStringBuilder(source) ;

}

SpannableStringBuilder实现了CharSequeSEO靠我nce, GetChars, Spannable, Editable,Appendable, GraphicsOperations的接口,内部的string是可以变化的。不过咱们修改afterTextSEO靠我Changed 中的参数s的时候,需要注意循环调用的潜在风险

,因为SpannableStringBuilder会在自己内部保存TextView的mChangeWatcher对象,代码1.3描述了设置过SEO靠我程。如代码所示,text通过setSpan方法添加了mChangeWatcher对象,以监听整个text的变化,并且做回调。当text的内容发生变化的时候,会通过span机制调用mChangeWatcSEO靠我her中的相应方法。 代码1.3 TextView的setText方法给mText添加mChangeWatcher的源码 Spannable sp SEO靠我= (Spannable) text ;

// Remove any ChangeWatchers that might have come from other TextViews.

final ChaSEO靠我ngeWatcher[] watchers = sp.getSpans( 0 , sp.length() , ChangeWatcher. class) ;

final int count = watcSEO靠我hers. length ;

for ( int i =  0 ; i < count ; i++) {

    sp.removeSpan(watchers[i]) ;

}

if ( mChangeWatcher SEO靠我==  null)  mChangeWatcher =  new ChangeWatcher() ;

sp.setSpan( mChangeWatcher ,  0 , textLength , SpaSEO靠我nned. SPAN_INCLUSIVE_INCLUSIVE |

        ( CHANGE_WATCHER_PRIORITY << Spanned. SPAN_PRIORITY_SHIFT)) ;

mChangeSEO靠我Watcher是一个ChangeWatcher对象,ChangeWatcher是TextView的内部类,实现了TextWatcher, SpanWatcher接口偶,代码1.4描述了ChangeWaSEO靠我tcher实现的TextWatcher的三个回调接口。这三个接口会分别调用TextView的相应的方法,通知所有注册的TextWatcher的调用相应的三个回调接口。其中,TextView的handlSEO靠我eTextChanged还会调用invalidate()和checkForResize()方法重绘UI界面. 代码1.4 ChangeWatcher实现的TextWatcheSEO靠我r的三个回调接口 public void  beforeTextChanged(CharSequence buffer , int start ,

int before , iSEO靠我nt after) {

    .......

    TextView. this.sendBeforeTextChanged(buffer , start , before , after) ;

}

public voiSEO靠我d  onTextChanged(CharSequence buffer , int start , int before , int after) {

    .......

TextView. this.haSEO靠我ndleTextChanged(buffer , start , before , after) ;

    ......

}

public void  afterTextChanged(Editable buffSEO靠我er) {

    ......

    TextView. this.sendAfterTextChanged(buffer) ;

    ......

}

通过SpannableStringBuilder,ChangeWatcheSEO靠我r这两个类再加上span机制,android可以在文本改变的时候通知所有注册的TextWatcher方法调用相应的三个接口。但是这又会使咱们在TextWatcher的afterTextChanged中SEO靠我修改参数s的时候,再次调用TextWatcher的三个回调接口,这样如果afterTextChanged不能因为某些条件的判断,终止对s的修改,那么就会形成无限循环调用。 类SEO靠我似TextView的setText的方法也会在相应的时机通知所有注册的TextWatcher调用响应的三个接口,如果咱们在TextWatcher的三个接口中调用TextView的setText方法也会SEO靠我导致无限循环调用。图1.3描述了无限调用的示例。

图1.3 无限调用示例

但是有时候我们可能 需要根据业务需求更改显示的文本,比如过滤不必要的字符,过滤非法的文字,添加必要的结束字符等。这个时候我们有时候会SEO靠我给TextView添加一个TextWatcher,然后在某个接口回调中根据传入的参数修改将要显示的文本,这可以满足咱们的业务需求,不过有可能会导致无限循环调用。针对这个问题,咱们可以通过InputFiSEO靠我lter来完成修改文本的目的

。 InputFilter是一个接口,内部定义了filter方法,这个方法的作用是修改传入的字符串,如果返回值为null,那么保持原来的字符串。代SEO靠我码1.5是这个方法的定义。 代码1.5 InputFilter内部定义了filter接口 public CharSequence  filter(CSEO靠我harSequence source , int start , int end ,

                           Spanned dest , int dstart , int dend) ;

如代码所示,filter方法需要传入六SEO靠我个参数,其中 source参数是即将替换选中字符区域的字符串对象; start参数表示source的起始位置; end参数SEO靠我表示source的终止位置。通过source,start,end这三个参数可以描述出替换选中字符区域的新的字符串。 dest表示选中文字区域的文本对象,TextView的seSEO靠我tText方法调用filter方法时,传入的dest为EMPTY_SPANNED,修改文字时,传入的 dest为TextView中保存的mText; SEO靠我 dstart表示选中的文字区域的起始位置; dend表示选中的文字区域的终止位置。 该方法的返回值是用来替换source作为新的替换文本。 SEO靠我 图1.4是有一个filter实例,描述了filter的六个参数的含义。 图1.4 filter实例   SEO靠我 InputFilter接口内部提供了两个子类,分别是AllCaps和LengthFilter。 AllCaps是将文本中的小写字符全部转为大写字符的过滤器,通过该过滤器SEO靠我,TextView能将输入文本中的小写字符转为大写字符,然后显示出来; LengthFilter是删除掉超过长度maxLength的字符的过滤器。在xml中配置了maxLenSEO靠我gth之后,TextView在创建实例的时候,会生成一个LengthFilter的过滤器,以便达到限定显示字符长度的功能。

咱们自己也可以定义满足咱们业务需求的inputfilter,以达到在TextWSEO靠我atcher接口回调之前过滤掉无用或者非法字符的功能

,代码1.6是一个InputFilter的实现子类,该类过滤掉无用的空白符。 代码1.6 过滤掉无用空白符的过滤器 SEO靠我 static class NoUsageCharInputFilter  implements InputFilter {

    @Override

public CharSequence SEO靠我 filter(CharSequence source , int start , int end , Spanned dest , int dstart , int dend) {

return soSEO靠我urce ==  null ?  null : source.toString().replaceAll( " \\ s" ,  "") ;

    }

}

定义完InputFilter的实现子类之后,咱们就可以将SEO靠我实现了的过滤器添加到TextView的过滤器数组中,代码1.7是一个添加过滤器的示例,如代码所示,通过source.setFilters(inputFilters)方法可以给TextView设置InpSEO靠我utFilter数组,由于我们的功能是添加过滤器,因此需要将source原本的过滤器数组中的元素添加到新的过滤器数组中,否则source原本的过滤器数组会被覆盖掉

,那样即使咱们在xml文件中配置了maSEO靠我xLength,source也不会使用LengthInputFilter来限定文本的长度。 代码1.7   添加过滤器的示例 public statSEO靠我ic void  addNoUsageCharInputFilter(TextView source) {

    if (source ==  null)

        return;

InputFilter[] inputFSEO靠我ilters =  new InputFilter[source.getFilters() !=  null ? source.getFilters(). length +  1:  1] ;

inpuSEO靠我tFilters[ 0] =  new NoUsageCharInputFilter() ;

    if (source.getFilters() !=  null) {

for ( int i =  0 ; SEO靠我i < source.getFilters(). length ; i++)

            inputFilters[i +  1] = source.getFilters()[i] ;

    }

source.setFiltSEO靠我ers(inputFilters) ;

}

TexView在设置完过滤器数组之后,它的setText方法会在调用sendBeforeTextChanged之前先用过滤器数组中的过滤器修改传入的文本参数,sSEO靠我etText方法调用过滤器的实现见代码1.8。 代码1.8 TexView中setText调用过滤器的实现代码 int n = mFilters.lSEO靠我ength; for (int i = 0; i < n; i++) {CharSequence out = mFilters[i].filter(text, 0, text.SEO靠我length(), EMPTY_SPANNED, 0, 0); if (out != null) {text = out; } SEO靠我}if (notifyBefore) {if (mText != null) {oldlen = mText.length(); sendBeforeTextChanged(mSEO靠我Text, 0, oldlen, text.length()); } else {sendBeforeTextChanged("", 0, 0, text.length());SEO靠我 } }

通过以上的分析: 1.咱们可以使用TextWatcher监听TextView文本变化的三个时机,并在回调函数中做相应的处理; 2.但是在回调函数中处SEO靠我理业务的时候,需要注意不要调用该TextView的setText方法,否则会发生无限循环调用; 3.在TextWatcher 的回调接口afterTextChanged方法中修改参数s的时候,也要注意在SEO靠我修改了s之后,会触发文本变化,导致TextView中所有注册的TextWatcher再次回调自己的的三个回调函数,此时需要预防无限循环调用的发生。 4.如果需要修改传入文本,那么可以实现InputFilSEO靠我ter接口,然后给TextView添加符合业务需求的过滤器; 5.给TextView添加自定义的过滤器的时候,需要注意使用setFilter方法设置过滤器会覆盖掉TextView原本的过滤器,如果不想舍SEO靠我弃TextView原本的过滤器,那么需要将原本的过滤器添加到新的过滤器数组中。 6.TextView使用InputFilter过滤器数组的时候,是从第一个过滤器到最后一个过滤器依次使用的,因此咱们给TeSEO靠我xtView设置过滤器数组的时候需要考虑过滤器的顺序。
“SEO靠我”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与 我们联系删除或处理,客服邮箱:html5sh@163.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同 其观点或证实其内容的真实性。

网站备案号:浙ICP备17034767号-2