美文网首页
从SetText()中学android(二)

从SetText()中学android(二)

作者: 桥寻 | 来源:发表于2017-04-12 09:36 被阅读460次
关于调度器

我们在长按edittext,或者在设置了textview的setTextIsSelectable(true)的时候,会蹦出来一个显示复制|粘贴|全选的窗口,类似这样

调度器

关闭选择调度器后,这个将不会显示,同理,在一个edittext中,我们可以移动光标位置来决定输入位置,这是通过插入调度器来决定的

handleView是一个Editor内部的实现了TextViewPositionListener接口的私有抽象类,暂不做分析

 hideInsertionPointCursorController();
 //执行到这里,将控制器隐藏了
if (mInsertionPointCursorController != null) {
  //@link Editor#InsertionPointCursorController#onDetached
    mInsertionPointCursorController.onDetached();
    mInsertionPointCursorController = null;
  //先将控制器hide,如果控制器不为null,直接回收掉 
  }
}
 //同上,套路都是一样的
 if (!mSelectionControllerEnabled) {
     stopTextActionMode();
      if (mSelectionModifierCursorController != null) {       
          mSelectionModifierCursorController.onDetached(); 
          mSelectionModifierCursorController = null;
      }
    }
  }
Editor#isCursorVisible
boolean textCanBeSelected() {
    // prepareCursorController() relies on this method.
    // If you change this condition, make sure prepareCursorController is called anywhere
    // the value of this condition might be changed. 
    if (mMovement == null || !mMovement.canSelectArbitrarily()) 
        return false; 
    return isTextEditable() || (isTextSelectable() && mText instanceof Spannable && isEnabled()); 
}
Editor#onDetached
 public void onDetached() {
    final ViewTreeObserver observer = mTextView.getViewTreeObserver();     
    observer.removeOnTouchModeChangeListener(this); 
    if (mHandle != null) 
        mHandle.onDetached(); 
  }

view#requestLayout()

从源码注释可以看出,如果当前View在请求布局的时候,View树正在进行布局流程的话,该请求会延迟到布局流程完成后或者绘制流程完成且下一次布局发现的时候再执行。
总的来说,当view需要重新布局时调用此方法

  public void requestLayout() {
    if (mMeasureCache != null) mMeasureCache.clear();

    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
        // Only trigger request-during-layout logic if this is the view requesting it,
        // not the views in its parent hierarchy
        ViewRootImpl viewRoot = getViewRootImpl();
        if (viewRoot != null && viewRoot.isInLayout()) {
            if (!viewRoot.requestLayoutDuringLayout(this)) {
                return;
            }
        }
        mAttachInfo.mViewRequestingLayout = this;
    }

    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
     //需要重新布局
    mPrivateFlags |= PFLAG_INVALIDATED;

    if (mParent != null && !mParent.isLayoutRequested()) {
        mParent.requestLayout();
        //使用父类的requestLayout,父类也设置标记位,这样层层上传,直到根view,然后根view传递给ViewRootImpl
    }
    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
        mAttachInfo.mViewRequestingLayout = null;
    }
}

invalidate

public void invalidate() { invalidate(true); }
void invalidate(boolean invalidateCache) { 
  invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); 
}

这里遍历mFilters,inputFilter是用于屏蔽某些字的接口,这里,我们将text轮流放置于filter中,遍历整个数组,最后得出text即为我们所要的text

 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; 
  }
 }

重新回到settext函数>notifyBefore,会遍历你的textwatcher的list,然后回调函数beforeTextChanged

if (notifyBefore) { 
  if (mText != null) {
     oldlen = mText.length();
     sendBeforeTextChanged(mText, 0, oldlen, text.length());
   }else { 
      sendBeforeTextChanged("", 0, 0, text.length()); 
   }
 }

控制是否需要在text改变后通知,默认不通知

 boolean needEditableForNotification = false; //是否需要通知
   if (mListeners != null && mListeners.size() != 0) { 
  //mListeners为textwatcher(text的观察者,在text改变后作出回调)的链表
  //如果text有观察者,则在改变后会作出相应
   needEditableForNotification = true; }
   if (type == BufferType.EDITABLE || getKeyListener() != null || needEditableForNotification){
  //getKeyListener返回的是当前textview所对应的Editor的keylistener

关于keyListener,keyListener是android.text.method包中的一个接口

Paste_Image.png

这段话的意思是:这是一个用来基于可编辑的类,将text的key事务转换成可编辑的操作。注意,在大多数情况下该接口已被一般软输入方法所取代,如android.view.inputmethod.InputMethod,它应该只用于其中应用程序有它自己的屏幕上的键盘,也希望处理硬键盘事件,与之相匹配的情况。>软键盘上的按键不会触发这个方法,android4.1或之后版本的键盘将不会触发。在android4.0或之前的版本来提供按键。

createEditorIfNeeded();//如果mEditor为null,创建一个Editor
mEditor.forgetUndoRedo();//将撤销和重新编辑全部取消
Editable t = mEditableFactory.newEditable(text);
 text = t; //使用filter来过滤Editable对象t 
setFilters(t, mFilters); 
InputMethodManager imm = InputMethodManager.peekInstance(); //检索全局的InputMethodManager是否存在

InputMethodManager是一个管理输入法面板的类

//InputMethodManager如果已经连接了某一个view,则重新启动输入法。 
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);
 } //对text的处理
 if (mAutoLinkMask != 0) {

mAutoLinkMask-->自动链接(如,text中有网址链接,自动标识)

Spannable s2;//首先声明一个Spannable

我们可以通过Spannable的实现类SpannableString来设置textview的各种样式

使用spannablestring的setSpan(Object what, int start, int end, int flags)便可以对textview设置想要的效果。其中what共有几种:

  • 样式 TypefaceSpan
  • 绝对大小 AbsoluteSizeSpan(可以使用px或者dp)
  • 相对大小RelativeSizeSpan(当前字体的多少倍)
  • 前景色和字体背景色 ForegroundColorSpan
  • 设置字体的粗体,斜体,粗斜体 StyleSpan
  • 设置下划线和删除线 UnderlineSpanStrikethroughSpan
  • 设置下标和上标 SubscriptSpanSuperscriptSpan
  • 设置超链接 URLSpan(需要添加setMovementMethod方法来附加相应)* 横向拉伸字体 ScaleXSpan
 if (type == BufferType.EDITABLE || text instanceof Spannable) { 
    s2 = (Spannable) text; 
  } else {
     s2 =  mSpannableFactory.newSpannable(text); 
  } if (Linkify.addLinks(s2, mAutoLinkMask)) { 
    text = s2;
    type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
    //如果type不是BufferType.EDITABLE 则将其改为BufferType.SPANNABLE 
    mText = text;
    if (mLinksClickable && !textCanBeSelected()) {
 setMovementMethod(LinkMovementMethod.getInstance());
     }
   }
 }

样式设置完成
TransformationMehods是这样一个类,textview可以使用它来做如用替换密码,或者保持新的被换行符拆分开的行
而getTransformation获得的是源文本
资源的转换(例如:用来替换密码,返回的长度和原来的长度必须相符合。如果以前的文本是Editable,则返回的必须是反应其动态的,不能仅仅做一次复制)

   mBufferType = type; 
   mText = text;
   //将text和type都存起来
   if (mTransformation == null) { 
    mTransformed = text;
   } else {
    mTransformed = mTransformation.getTransformation(text, this); 
    } 
  final int textLength = text.length();
//记录下textLength的长度 
  if (text instanceof Spannable && !mAllowTransformationLengthChange) {
 
    Spannable sp = (Spannable) text; 
    // 移除所有可能来自其他textview的changewatcher

    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)); 
   if (mEditor != null)     
      mEditor.addSpanWatchers(sp);
   if (mTransformation != null) { 

   sp.setSpan(mTransformation, 0, textLength,  Spanned.SPAN_INCLUSIVE_INCLUSIVE);
  } 
    if (mMovement != null) { 
      mMovement.initialize(this, (Spannable) text);
   
    if (mEditor != null)
     mEditor.mSelectionMoved = false;
  }
}

if (mLayout != null) {
  checkForRelayout();
}
sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
if (needEditableForNotification) {   
  sendAfterTextChanged((Editable) text); 
}
// SelectionModifierCursorController depends on textCanBeSelected, which depends on text 
if (mEditor != null)   
   mEditor.prepareCursorControllers();

参考博客:http://www.liuguangli.win/archives/476http://blog.csdn.net/flowingflying/article/details/9950797https://developer.android.com/reference/android/text/method/TransformationMethod.html

相关文章

网友评论

      本文标题:从SetText()中学android(二)

      本文链接:https://www.haomeiwen.com/subject/xhqtuttx.html