Android之自由选择TextView的文字(转)

用过EditText的都知道,EditText有个特点,当在里面长按的时 候,会出现一个ContextMenu,提供了选择文字,复制,剪切等功能。有时候,我们会想,如果不出现这个ContextMenu,直接就在view 上选择文字,那多美好啊。相信很多人抱有这样的想法,很不幸,我也是。于是我就研究了一下EditText和TextView的代码,然后将这个问题解决 了。
网上很多资料都说,要选择一段文字,只需要用Selection.getSelectionStart()和 Selection.getSelectionEnd()确定选择的文字的头和尾,然后加颜色就行。简直是胡扯啊,我敢说这样的代码根本就没有经过验证, 就发到网上了,然后一大堆人互相转载,结果导致误导了很多人,杯具 啊!!
好,我们来分析一下解决办法。
TextView是很多View的基类,如Button、EditText都是继承自他,所以EditText里面的代码很少。我们看一下 EditText的源码,有一个Override的getDefaultEditable方法,看名字的意思是是否可编辑,这个方法直接返回true。还 有一个getDefaultMovementMethod方法,它返回的是ArrowKeyMovementMethod.getInstance(), 通过查看ArrowKeyMovementMethod的源码,基本确定这个方法就是弹出ContextMenu和轨迹球监听的“元凶”。
下面,我们自己做一个view来打造自己的EditText。
我取名TextPage,继承EditText,在里面覆盖getDefaultEditable和getDefaultMovementMethod。

[java]  view plain copy print ?
  1. @Override     
  2. public   boolean  getDefaultEditable() {    
  3.     return   false ;    
  4. }    
  5. @Override     
  6. protected  MovementMethod getDefaultMovementMethod() {    
  7.     return   null ;    
  8. }  

 

现在测试一下,发现长按没反应了,所料不错,就是 getDefaultMovementMethod方法控制了ContextMenu。
看一下ArrowKeyMovementMethod的代码,里面提供了KeyEvent、轨迹球事件onTrackballEvent和touch事件 onTouchEvent的处理。这些事件在何处调用的呢?我们看看TextView的onTouchEvent、onTrackballEvent和 onKeyEvent方法里面就明白了,在这些事件回调中调用了ArrowKeyMovementMethod里面的这些方法。
还有个问题,ContextMenu在哪里触发的?这个问题,用过ContextMenu的都知道,view里面要使用ContextMenu,需要覆盖 一个onCreateContextMenu方法,然后在里面创建ContextMenu的各个选项。在TextView里面找 onCreateContextMenu,果然有,里面定义了选择、复制、粘贴等选项。
既然找到了这个,那么我们就可以进一步分析选择是如何做到的。
onCreateContextMenu只是创建菜单,那么菜单点击之后,触发了什么呢?onCreateContextMenu里面定义了一个 MenuHandler对象,然后作为参数传递给setOnMenuItemClickListener,找到MenuHandler,发现里面的 onMenuItemClick返回的是onTextContextMenuItem函数,找到onTextContextMenuItem,OMG,终 于找到点击menu触发的函数了。但是里面貌似没有关键的东西,选择的部分不在这里。那么,就应该在上面所说的那些事件里面了。
重点分析ArrowKeyMovementMethod的onTouchEvent方法。发现一个重要的方法getLayout(),然后获取一个 Layout对象,通过x和y坐标知道当前字符串的offset位置。
那么,问题就可以完美的解决了。你可以点击任何地方然后拖动,释放之后,中间的文字就会被选中,so beautiful!

[java]  view plain copy print ?
  1. import  android.content.Context;    
  2. import  android.graphics.Color;    
  3. import  android.text.Layout;    
  4. import  android.text.Selection;    
  5. import  android.view.ContextMenu;    
  6. import  android.view.Gravity;    
  7. import  android.view.MotionEvent;    
  8. import  android.widget.EditText;    
  9.    
  10. /**   
  11.  * @author chroya   
  12.  */     
  13. public   class  TextPage  extends  EditText {    
  14.     private   int  off;  //字符串的偏移值     
  15.    
  16.     public  TextPage(Context context) {    
  17.         super (context);    
  18.         initialize();    
  19.     }    
  20.    
  21.     private   void  initialize() {    
  22.         setGravity(Gravity.TOP);    
  23.         setBackgroundColor(Color.WHITE);    
  24.     }    
  25.    
  26.     @Override     
  27.     protected   void  onCreateContextMenu(ContextMenu menu) {    
  28.         //不做任何处理,为了阻止长按的时候弹出上下文菜单     
  29.     }    
  30.    
  31.     @Override     
  32.     public   boolean  getDefaultEditable() {    
  33.         return   false ;    
  34.     }    
  35.    
  36.     @Override     
  37.     public   boolean  onTouchEvent(MotionEvent event) {    
  38.         int  action = event.getAction();    
  39.         Layout layout = getLayout();    
  40.         int  line =  0 ;    
  41.         switch (action) {    
  42.         case  MotionEvent.ACTION_DOWN:    
  43.             line = layout.getLineForVertical(getScrollY()+ (int )event.getY());            
  44.             off = layout.getOffsetForHorizontal(line, (int )event.getX());    
  45.             Selection.setSelection(getEditableText(), off);    
  46.             break ;    
  47.         case  MotionEvent.ACTION_MOVE:    
  48.         case  MotionEvent.ACTION_UP:    
  49.             line = layout.getLineForVertical(getScrollY()+(int )event.getY());     
  50.             int  curOff = layout.getOffsetForHorizontal(line, ( int )event.getX());                
  51.             Selection.setSelection(getEditableText(), off, curOff);    
  52.             break ;    
  53.         }    
  54.         return   true ;    
  55.     }    
  56. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值