Android開發(fā):自由選擇TextView的文字
我們介紹過一系列Android開發(fā)的教程,比如《如何實(shí)現(xiàn)TCP和UDP傳輸》、《在MyEclipse 8.6上搭建Android開發(fā)環(huán)境》,今天我們來介紹一下在Android下自由選擇TextView的文字。
51CTO推薦專題:Android開發(fā)應(yīng)用詳解
用過EditText的都知道,EditText有個(gè)特點(diǎn),當(dāng)在里面長(zhǎng)按的時(shí)候,會(huì)出現(xiàn)一個(gè)ContextMenu,提供了選擇文字、復(fù)制、剪切等功能。如果不出現(xiàn)這個(gè)ContextMenu,直接就在view上選擇文字,那樣會(huì)更加方便。于是作者就研究了一下EditText和TextView的代碼,然后將這個(gè)問題解決了。
網(wǎng)上很多資料都說,要選擇一段文字,只需要用Selection.getSelectionStart()和Selection.getSelectionEnd()確定選擇的文字的頭和尾,然后加顏色就行。作者經(jīng)過測(cè)試,發(fā)現(xiàn)這個(gè)結(jié)果導(dǎo)致誤導(dǎo)了很多人,是行不通的。
我們來分析一下解決辦法。
TextView是很多View的基類,如Button、EditText都是繼承自他,所以EditText里面的代碼很少。我們看一下EditText的源碼,有一個(gè)Override的getDefaultEditable方法,看名字的意思是是否可編輯,這個(gè)方法直接返回true。還有一個(gè)getDefaultMovementMethod方法,它返回的是ArrowKeyMovementMethod.getInstance(),通過查看ArrowKeyMovementMethod的源碼,基本確定這個(gè)方法就是彈出ContextMenu和軌跡球監(jiān)聽的“元兇”。
下面,我們自己做一個(gè)view來打造自己的EditText。
我取名TextPage,繼承EditText,在里面覆蓋getDefaultEditable和getDefaultMovementMethod。
Java代碼
- @Override
- public boolean getDefaultEditable() {
- return false;
- }
- @Override
- protected MovementMethod getDefaultMovementMethod() {
- return null;
- }
現(xiàn)在測(cè)試一下,發(fā)現(xiàn)長(zhǎng)按沒反應(yīng)了,所料不錯(cuò),就是getDefaultMovementMethod方法控制了ContextMenu。
看一下ArrowKeyMovementMethod的代碼,里面提供了KeyEvent、軌跡球事件onTrackballEvent和touch事件onTouchEvent的處理。這些事件在何處調(diào)用的呢?我們看看TextView的onTouchEvent、onTrackballEvent和onKeyEvent方法里面就明白了,在這些事件回調(diào)中調(diào)用了ArrowKeyMovementMethod里面的這些方法。
還有個(gè)問題,ContextMenu在哪里觸發(fā)的?這個(gè)問題,用過ContextMenu的都知道,view里面要使用ContextMenu,需要覆蓋一個(gè)onCreateContextMenu方法,然后在里面創(chuàng)建ContextMenu的各個(gè)選項(xiàng)。在TextView里面找onCreateContextMenu,果然有,里面定義了選擇、復(fù)制、粘貼等選項(xiàng)。
既然找到了這個(gè),那么我們就可以進(jìn)一步分析選擇是如何做到的。
onCreateContextMenu只是創(chuàng)建菜單,那么菜單點(diǎn)擊之后,觸發(fā)了什么呢?
onCreateContextMenu里面定義了一個(gè)MenuHandler對(duì)象,然后作為參數(shù)傳遞給setOnMenuItemClickListener,找到MenuHandler,發(fā)現(xiàn)里面的onMenuItemClick返回的是onTextContextMenuItem函數(shù),找到onTextContextMenuItem,OMG,終于找到點(diǎn)擊menu觸發(fā)的函數(shù)了。但是里面貌似沒有關(guān)鍵的東西,選擇的部分不在這里。那么,就應(yīng)該在上面所說的那些事件里面了。
重點(diǎn)分析ArrowKeyMovementMethod的onTouchEvent方法。發(fā)現(xiàn)一個(gè)重要的方法getLayout(),然后獲取一個(gè)Layout對(duì)象,通過x和y坐標(biāo)知道當(dāng)前字符串的offset位置。
那么,問題就可以完美的解決了。你可以點(diǎn)擊任何地方然后拖動(dòng),釋放之后,中間的文字就會(huì)被選中。
Java代碼
- import android.content.Context;
- import android.graphics.Color;
- import android.text.Layout;
- import android.text.Selection;
- import android.view.ContextMenu;
- import android.view.Gravity;
- import android.view.MotionEvent;
- import android.widget.EditText;
- /**
- * @author chroya
- */
- public class TextPage extends EditText {
- private int off; //字符串的偏移值
- public TextPage(Context context) {
- super(context);
- initialize();
- }
- private void initialize() {
- setGravity(Gravity.TOP);
- setBackgroundColor(Color.WHITE);
- }
- @Override
- protected void onCreateContextMenu(ContextMenu menu) {
- //不做任何處理,為了阻止長(zhǎng)按的時(shí)候彈出上下文菜單
- }
- @Override
- public boolean getDefaultEditable() {
- return false;
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- int action = event.getAction();
- Layout layout = getLayout();
- int line = 0;
- switch(action) {
- case MotionEvent.ACTION_DOWN:
- line = layout.getLineForVertical(getScrollY()+ (int)event.getY());
- off = layout.getOffsetForHorizontal(line, (int)event.getX());
- Selection.setSelection(getEditableText(), off);
- break;
- case MotionEvent.ACTION_MOVE:
- case MotionEvent.ACTION_UP:
- line = layout.getLineForVertical(getScrollY()+(int)event.getY());
- int curOff = layout.getOffsetForHorizontal(line, (int)event.getX());
- Selection.setSelection(getEditableText(), off, curOff);
- break;
- }
- return true;
- }
- }
【編輯推薦】