Android开发:自由选择EditText的文字

来源:互联网 发布:台湾海关数据 编辑:程序博客网 时间:2024/06/10 14:51

用过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代码

  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位置。

那么,问题就可以完美的解决了。你可以点击任何地方然后拖动,释放之后,中间的文字就会被选中。

Java代码

  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. public class TextPage extends EditText    
  12.     private int off; //字符串的偏移值    
  13.     
  14.     public TextPage(Context context)    
  15.         super(context);    
  16.         initialize();    
  17.        
  18.     
  19.     private void initialize()    
  20.         setGravity(Gravity.TOP);    
  21.         setBackgroundColor(Color.WHITE);    
  22.        
  23.         
  24.     @Override    
  25.     protected void onCreateContextMenu(ContextMenu menu)    
  26.         //不做任何处理,为了阻止长按的时候弹出上下文菜单    
  27.        
  28.         
  29.     @Override    
  30.     public boolean getDefaultEditable()    
  31.         return false;    
  32.        
  33.         
  34.     @Override    
  35.     public boolean onTouchEvent(MotionEvent event)    
  36.         int action event.getAction();    
  37.         Layout layout getLayout();    
  38.         int line 0   
  39.         switch(action)    
  40.         case MotionEvent.ACTION_DOWN:    
  41.             line layout.getLineForVertical(getScrollY()+ (int)event.getY());            
  42.             off layout.getOffsetForHorizontal(line, (int)event.getX());    
  43.             Selection.setSelection(getEditableText(), off);    
  44.             break;    
  45.         case MotionEvent.ACTION_MOVE:    
  46.         case MotionEvent.ACTION_UP:    
  47.             line layout.getLineForVertical(getScrollY()+(int)event.getY());     
  48.             int curOff layout.getOffsetForHorizontal(line, (int)event.getX());                
  49.             Selection.setSelection(getEditableText(), off, curOff);    
  50.             break;    
  51.            
  52.         return true;    
  53.        
  54.    
原创粉丝点击