Spannable、Spanned、Editable用法及差别

裴韬
2023-12-01
一、接口定义
1.Spanned
     这是一个针对文本的接口,用来标记在文本的某些范围之类,附属了哪些对象。
      public interface Spanned extends CharSequence
     该接口是继承了CharSequence,所以在android平台可以直接当做CharSequence来使用,
并且增加了很多特殊的文本处理功能。
     该接口定义了很多Flag变量,注意以下几个常用的flag标记,用来标记范围
     SPAN_INCLUSIVE_EXCLUSIVE
     SPAN_INCLUSIVE_INCLUSIVE
     SPAN_EXCLUSIVE_EXCLUSIVE
     SPAN_EXCLUSIVE_INCLUSIVE

     主要的几个接口,都是用来获取标记范围参数的:
     public int getSpanStart(Object tag);
     public int getSpanEnd(Object tag);
     public int getSpanFlags(Object tag);
     public <T> T[] getSpans(int start, int end, Class<T> type);

2.Spannable
     public interface Spannable extends Spanned
     该接口继承自Spanned,包含2个接口方法,和一个工厂类
     
     public void setSpan(Object what, int start, int end, int flags);
     public void removeSpan(Object what);

     该接口定义了一个工厂类,首先想到的是减少与实现类的耦合,提高可维护性。
你可以定义自己的工厂类,来实现不同的目的。里面很巧妙的运用了一个静态内部类,
既实现了Lazy Loading的单例模式,又巧妙解决了多线程安全的问题。这里可以借鉴,
静态内部类实现的单例模式
     public static class Factory {
     
          private static Spannable.Factory sInstance = new Spannable.Factory();
          
          public static Spannabe.Factory getInstance() {
               return sInstance;
          }
          
          public Spannable newSpannable(CharSequence source) {
               return new SpannableString(source);
          }
     }

3.Editable
      public interface Editable extends Spannable, Appendable, CharSequence, GetChars
     可以看到该接口主要继承了Spannable,Appendable这2个接口,所以Editable既具有Spannable的功能,又能改变text的内容。
     主要的几个接口为:
     
     public Editable replace(int st, int en, CharSequence source, int start, int end);
     public Editable replace(int st, int en, CharSequence text);     //replace(st, en, text, 0, text.length());
     public Editable insert(int where, CharSequence text, int start, int end);
     public Editable delete(int st, int en);         //replace(st, en, "", 0, 0);
     public Editable append(CharSequence text);     //replace(length(), length(), text, 0, text.length());
     
     //对输入的内容可进行过滤限制
      public void setFilters(InputFilter[] filters);
     public InputFilter[] getFilters();

     同样的该接口也定义了一个工厂类,与Spannable类似。
     ……
     ……

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


二、TextView.BufferType
     TextView控件设置文本时,有个参数叫TextView.BufferType,这是个枚举类,共有3个枚举值
     public enum BufferTpe {
          NORMAL, SPANNABLE, EDITABLE
     }

     在TextView中,默认的bufferType为BufferType.NORMAL,即默认的bufferType既不是Spannable,也不是Editable:
      private  BufferType  mBufferType  = BufferType. NORMAL ;

     有2个工厂对象,都是系统默认值:

   private Editable.Factory mEditableFactory = Editable.Factory.getInstance();

    private  Spannable.Factory  mSpannableFactory  = Spannable.Factory.getInstance();
     
     在TextView中的setText()方法:
     setText(CharSequence text, BufferType type);
     setText(CharSequence text);          //如果不设置bufferType,会采用默认的bufferType,即BufferType.NORMAL
   
     其中关于BufferType的主要代码:
     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);
        }
     可以看出根据不同的BufferType,用类工厂的模式创建出不同的text,默认情况下,类工厂都是系统默认定义好的,如:mEditableFactory、mSpannableFactory。
     TextView中提供了更换Factory的可能,使得开发者能够根据需求自定义:
     public final void setEditableFactory(Editable.Factory factory) {
        mEditableFactory = factory;
        setText(mText);
    }
    public final void setSpannableFactory(Spannable.Factory factory) {
        mSpannableFactory = factory;
        setText(mText);
    }

    
    在EditText中,setText()时, 默认已经把bufferType设置为Editable了
    public void setText(CharSequence text, BufferType type) {
           super.setText(text, BuffetType.EDITABLE);
    }    
    所以,EditText的getText(),得到的也是Editable类型:
    public Editable getText() {
            return (Editable)super.getText();
    }


三、代码实例
1.利用Spannable设置各种效果
     Spannable span = Spannable.Factory.getInstance().newSpannable("Click here to see more.");
     span.setSpan(new UnderlineSpan(), 0, 5, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
     tv.setText(span);
出现的效果:在"Click"下面会出现下划线, Click here to see more. 
主要方法:setSpan(Object what, int start, int end, int flags);
     start:起始位置, inclusive
     end:结束位置, exclusive,最终起效果的字符长度为 end-start
     flags: 其主要含义为标识在Span范围内的文本前后输入新的字符时,是否把它们也应用于这个效果。

     SPAN_INCLUSIVE_INCLUSIVE效果:
      Click here to see more.       //原始应用效果,在Click下面会有下划线
      aClick here to see more.     //在Click之前输入一个字符a,则a也会有下划线 
      aClicka here to see more.   //在Click之后输入一个字符a,a也有下划线   

     SPAN_INCLUSIVE_EXCLUSIVE:
      Click here to see more.
      aClick here to see more.
      aClicka here to see more.     //在Click之后输入一个字符a,a并没有下划线
2.利用TextView的BufferType设置文本内容,
     TextView tv = ….;
     tv.setText("Text", BufferType.EDITABLE);
     Editable editable = (Editable) tv.getText();
     editable.append(" append test.");
     在UI上的显示依次为:
     Text
     Text append test.
     注意在setText()时,一定要把BufferType设置为BufferType.EDITABLE,否则getText()得到不是Editable类型对象。


四、总结
1.Spannable可以在给定的字符区域内使用各种样式;
2.Editable继承自Spannable,同样也可以在给定的字符区域内使用各种样式;
3.Editable类似于StringBuffer可以增加、删除、修改字符,也就是getText()后可调用append方法设置修改文本内容;
4.TextView的setText(),默认的BufferType为BufferType.NORMAL;
5.EditText的setText(),已经将BuffetType写死为BufferType.EDITABLE;
6.TextView的getText()返回类型为CharSequence;
7.EditText的getText()返回类型为Editable;



 类似资料: