I'm wanting to implement a custom text interface, with touch+drag selecting text and the keyboard not being raised, in contrast to the default behavior of a long-click bringing up the CCP menu and the keyboard. My understanding suggests I need this approach:
onTouchEvent(event){
case touch_down:
get START text position
case drag
get END text position
set selection range from START to END
}
I've found out all about getSelectStart() and various methods to setting a range and such, but I cannot find how to get the text position based on a touch event getX() and getY(). Is there any way to do this? I've seen the behaviour I want in other office apps.
Also, how would I stop the keyboard appearing until manually requested?
"mText.setInputType(InputType.TYPE_NULL)" will suppress the soft keyboard but it also disables the blinking cursor in an EditText box under Android 3.0 and above. I coded an onTouchListener and returned true to disable the keyboard and then had to get the touch position from the motion event to set the cursor to the correct spot. You might be able to use this on an ACTION_MOVE motion event to select text for dragging.
Here is the code I used:
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);
Thanks Waine Kail for sharing the start code, but it only handled the "x" axis of the event. For multiline EditText, you also must:
1- Calculate the vertical position (y):
2- Get the line offset by the vertical position
3- Get the text offset using the line and horizontal position
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;
}
});
I'm facing the same issue too. And when I tried to use "int offset = layout.getOffsetForHorizontal(0, x);" like Wayne Kail said, also got NPE in this line. So I tried and in the end write like this:
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);
For my application I needed to get the position within a touchlistener but the above solution appears to not give a precise location. For me it more often than not was off by 1-2 chars relative to the text cursor. The below code solves this. The key to get this to work is to use post() to postpone the code until after the cursor position has been updated. Thus the position I get is guaranteed to agree with that of EditText, and the code is also simpler.
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;
}
});