Android:如何从触摸事件中获取文本位置

9

我想实现一个自定义文本界面,能够用触摸+拖拽选择文本,而且不会弹出键盘。这与默认行为不同,因为长按会弹出CCP菜单和键盘。我的理解是需要采用以下方法:

onTouchEvent(event){
  case touch_down:
    get START text position

  case drag
    get END text position
    set selection range from START to END
}

我已经了解了getSelectStart()和各种设置范围等方法,但我找不到如何基于触摸事件的getX()和getY()来获取文本位置的方法。有没有办法做到这一点?我在其他办公应用程序中看到了我想要的行为。

此外,如何在手动请求之前阻止键盘出现?

5个回答

13

"mText.setInputType(InputType.TYPE_NULL)"可以禁止Android 3.0及以上版本中EditText框中的软键盘,但它也会禁用光标闪烁。我编写了一个onTouchListener并返回true以禁用键盘,然后必须从motion event获取触摸位置来设置光标到正确的位置。您可能可以在ACTION_MOVE motion事件上使用此功能以选择文本进行拖动。

这是我使用的代码:

  mText = (EditText) findViewById(R.id.editText1);
  OnTouchListener otl = new OnTouchListener() {
  @Override
  public boolean onTouch(View v, MotionEvent event) {
      switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
          Layout layout = ((EditText) v).getLayout();
          float x = event.getX() + mText.getScrollX();
          int offset = layout.getOffsetForHorizontal(0, x);
          if(offset>0)
              if(x>layout.getLineMax(0))
                  mText.setSelection(offset);     // touch was at end of text
              else
                  mText.setSelection(offset - 1);
          break;
          }
      return true;
      }
  };
  mText.setOnTouchListener(otl);

2
我在这一行代码上遇到了空指针异常: "int offset = layout.getOffsetForHorizontal(0, x);" - Nirav Mehta

11

感谢Waine Kail共享起始代码,但它仅处理了事件的“x”轴。对于多行EditText,您还必须执行以下步骤:

1- 计算垂直位置(y):
2- 通过垂直位置获取行偏移量
3- 使用行和水平位置获取文本偏移量

EditText et = (EditText) findViewById(R.id.editText1);

long offset = -1; //text position will be corrected when touching.

et.setOnTouchListener(new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
      case MotionEvent.ACTION_UP:
          Layout layout = ((EditText) v).getLayout();
          float x = event.getX() + et.getScrollX();
          float y = event.getY() + et.getScrollY();            
          int line = layout.getLineForVertical((int) y);                      

 // Here is what you wanted:

          offset = layout.getOffsetForHorizontal( line,  x);

          break;
        }
    return false;
}
});

谢谢,这很有帮助... :) - ADT
我在布局中遇到了空指针异常,为什么呢?我不知道。 - Dharmishtha
我之前为布局做了这样的事情。 ViewTreeObserver vto = edittext.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { layout = edittext.getLayout(); } }); - Dharmishtha

6

我也遇到了同样的问题。当我尝试像Wayne Kail所说的使用“int offset = layout.getOffsetForHorizontal(0, x);”时,也在这一行中得到了NPE。所以我尝试了一下,最终写成了这样:

mText = (EditText) findViewById(R.id.editText1);
OnTouchListener otl = new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
      EditText editText = (EditText) v;
      float x = event.getX();
      float y = event.getY();
      int touchPosition = editText.getOffsetForPosition(x, y);
      if (touchPosition>0){
          editText.setSelection(touchPosition);
      }
      return true;
    }
};
mText.setOnTouchListener(otl); 

2

对于我的应用程序,我需要在触摸监听器中获取位置,但是上述解决方案似乎不能提供精确的位置。对于我来说,它与文本光标相比经常偏移1-2个字符。以下代码解决了这个问题。使其正常工作的关键是使用post()将代码推迟到光标位置更新后。因此,我得到的位置保证与EditText一致,并且代码也更简单。

setOnTouchListener(new OnTouchListener()
    {
    @Override
    public boolean onTouch(View view, MotionEvent event)
        {
        if(event.getAction()==MotionEvent.ACTION_UP)
            {
            //At this point the selection has not been updated. So post an
            //event at the end of the event queue to check where cursor
            //has been moved
            post(new Runnable()
                {
                public void run()
                    {
                    Editable sb=getText();
                    int currentPos=getSelectionStart();
                    //TODO
                    }
                });
            }
        return false;
        }
    });

0
在 EditText 中,onTouch 事件会被调用两次:分别是 ACTION_DOWN 和 ACTION_UP。这就是为什么 getLayout 方法返回 null 的原因。我将在此发布可行的代码,并进行必要的确认和处理。
@Override
@SuppressLint("ClickableViewAccessibility")
public boolean onTouch(View view, MotionEvent motionEvent) {
    final EditText editText = (EditText) view;
    final int editTextSelectionStart = editText.getSelectionStart();
    final int editTextSelectionEnd = editText.getSelectionEnd();
    final Layout layout = editText.getLayout();
    final int inType = editText.getInputType(); // Backup the input type.
    editText.setInputType(InputType.TYPE_NULL); // Disable standard keyboard.
    editText.onTouchEvent(motionEvent); // Call native handler.
    editText.setInputType(inType);// Restore input type.

    switch (motionEvent.getAction()) {
        case MotionEvent.ACTION_DOWN:
            float x = motionEvent.getX() + editText.getScrollX();
            if (layout != null) {
                int offset = layout.getOffsetForHorizontal(0, x);
                if (offset > 0) {
                    if (x > layout.getLineMax(0)) {
                        editText.setSelection(offset);
                    } else {
                        editText.setSelection(offset - 1);
                    }
                }
            }
            break;
        case MotionEvent.ACTION_UP:
            editText.setSelection(editTextSelectionStart, editTextSelectionEnd);
        break;
    }

    return true; // Consume touch event.
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接