从SetText()中学android(二)

关于调度器

我们在长按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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,639评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,093评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,079评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,329评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,343评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,047评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,645评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,565评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,201评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,338评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,014评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,701评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,194评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,685评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,345评论 2 358

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,245评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,440评论 0 17
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,780评论 22 665
  • 今天7点就被婶婶给叫醒了,今天亲戚家做的房子封顶,叔叔和婶婶老早就起来了,然后到我楼下叫我。妹妹和叔叔去...
    天空蓝上阅读 164评论 0 0
  • 该方法主要用于将 UIColor 转化为 UIImage ,用于项目中,放在代码块中,方便使用
    Ice丶泽阅读 206评论 0 0