Android IME, set cursor position in EditText

2019-03-28 13:12发布

I am working on soft keyboard where I need to set the cursor position in IME edit text.

enter image description here

As shown in above image, I had created soft keyboard, as we can see some text is entered in edit text and current cursor position(shown by blue indicator).

I need to set the cursor position at the end of the current line (in our case at the end of the line first shown by red color in image)

I have tried with different functions provided with InputConnection, I tried with,

CharSequence seq = conn.getTextBeforeCursor(1000, 0);     // will get as much characters as possible on the left of cursor

And one more thing, I also need to count to the number of lines in edit text (in our case it's two).

4条回答
走好不送
2楼-- · 2019-03-28 13:20

Hi friends thanks for appreciation. Before two days I got my solution but can't able to update my answer.

For that I used below code,

If we are using api version greater than 10,

sendDownUpKeyEvents(0x0000007b);

because this method is added in api 11.

If we are using api version less than 11,

if (getCurrentInputConnection() != null) {
                    CharSequence textAfter = getCurrentInputConnection().getTextAfterCursor(1024, 0);
                    if (!TextUtils.isEmpty(textAfter)) {
                        int newPosition = 1;
                        while (newPosition < textAfter.length()) {
                            char chatAt = textAfter.charAt(newPosition);
                            if (chatAt == '\n' || chatAt == '\r') {
                                break;
                            }
                            newPosition++;
                        }
                        if (newPosition > textAfter.length())
                            newPosition = textAfter.length();
                        try {
                            CharSequence textBefore = getCurrentInputConnection().getTextBeforeCursor(Integer.MAX_VALUE, 0);
                            if (!TextUtils.isEmpty(textBefore)) {
                                newPosition = newPosition + textBefore.length();
                            }
                            getCurrentInputConnection().setSelection(newPosition, newPosition);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
查看更多
Emotional °昔
3楼-- · 2019-03-28 13:21

I'm sorry to say you wont be able to do that from a IME. For that purpose you should access some methods of the EditText, but there isn't there any possible API available, because the textview control is in an activity another process, not your process.

All the information you can get is what you have in the EditorInfo parameter that you can see in some InputMethodService methods, such as onStartInput(EditorInfo attribute, boolean restarting). Probably in the future the include some other way of accessing more info, but at this point, everything you can do os what you are asking for, is to change the cursor position with the setSelection method of InputConnection, but you also don't know which is the position of the end of the first line, so that wont be really useful for what you need.

查看更多
Deceive 欺骗
4楼-- · 2019-03-28 13:23

Some of the other answers seem overly complicated or incomplete. This is a general answer for future visitors.

In an activity

If you have a reference to EditText then it is easy.

Set the cursor position

editText.setSelection(index);

Set a selected range

editText.setSelection(startIndex, endIndex);

In an IME (keyboard)

It's a little more difficult from within an IME because you don't have direct access to the EditText. However, you can use the InputConnection to set the cursor position and selection.

The following answers get the input connection like this from within your InputMethodService subclass:

InputConnection inputConnection = getCurrentInputConnection();

Set the cursor position

inputConnection.setSelection(index, index);

Set a selected range

inputConnection.setSelection(startIndex, endIndex);

Move the cursor to the start

inputConnection.setSelection(0, 0);

Move the cursor to the end

ExtractedText extractedText = inputConnection.getExtractedText(new ExtractedTextRequest(), 0);
if (extractedText == null || extractedText.text == null) return;
int index = extractedText.text.length();
inputConnection.setSelection(index, index);

This method isn't guaranteed to work because the extracted text will not be be the entire text from the EditText if the text is very long. However, for most situations it is fine. Another option would be to use a combination of the following

  • inputConnection.getTextBeforeCursor(numberOfChars, 0)
  • inputConnection.getSelectedText(0)
  • inputConnection.getTextAfterCursor(numberOfChars, 0)

where numberOfChars is a large number.

Get current cursor index (or selection)

ExtractedText extractedText = inputConnection.getExtractedText(new ExtractedTextRequest(), 0);
int startIndex = extractedText.startOffset + extractedText.selectionStart;
int endIndex = extractedText.startOffset +  extractedText.selectionEnd;

In the case that the extractedText does not return the full text of the EditText, the startOffset tells you from what point it was pulled from. Then you can get the actual cursor index by adding the startOffset to the extracted text's selection start or end.

Move the cursor relative to its current position

Once you know the current position of the cursor, it is easy to move it around. Here is an example that moves the cursor to the beginning of the previous word.

BreakIterator boundary = BreakIterator.getWordInstance();
boundary.setText(extractedText.text.toString());
int preceding = boundary.preceding(extractedText.selectionStart);
int newIndex = (preceding == BreakIterator.DONE) ? selectionStart : preceding;
inputConnection.setSelection(newIndex, newIndex);

See other BreakIterator options here.

You can also move left, right, up and down by sending d-pad down up events.

Move left

inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT));
inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_LEFT));

Move right

inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT));
inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_RIGHT));

Move up

inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP));
inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_UP));

Move down

inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN));
inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_DOWN));
查看更多
Rolldiameter
5楼-- · 2019-03-28 13:37

Below is some working code to do what you are wanting.

It's quite simple:

To set the cursor position to the end of the first line, we first have to get the index of the last text character of that line. There is a method on the EditTexts' Layout to do this.

The Layout is what the edittext uses internally to layout the text, and it has a method called getLineEnd which returns the text index at the end of a line (we need to subtract 1 from this, otherwise our cursor would end of up at the start of the next line).

Once we have the position to set the cursor to, we just use the setSelection method of the EditText.

int endOfFirstLine = editText.getLayout().getLineEnd(0)-1;
//set the text selection (cursor postion) to that index
editText.setSelection(endOfFirstLine);

We can also get the number of lines really easily with

int lineCount = editText.getLineCount();

The trick with this code, is that it has to be run after the EditText's Layout has been built. That means that if we just put this in our onCreate or onResume, it wouldn't work. The line count would be 0.

So we have to run this code after the layout pass has been completed. Which we do by adding a layout listener to the edittext, and running the code after its complete.

editText.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override public void onGlobalLayout() {
}

Heres the full activity code:

public class MainActivity extends Activity {

    private EditText editText;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editText = (EditText) findViewById(R.id.test_edittext);

        editText.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override public void onGlobalLayout() {

                //this gives us the index of the text at the end of the first line.
                int endOfFirstLine = editText.getLayout().getLineEnd(0)-1;
                //set the text selection (cursor postion) to that index
                editText.setSelection(endOfFirstLine);

                //we can get the line count with getLineCount
                int lineCount = editText.getLineCount();

                Toast.makeText(MainActivity.this, "number of edittext lines: " + lineCount, Toast.LENGTH_LONG).show();
            }
        });

    }

    @Override protected void onResume() {
        super.onResume();



    }

}
查看更多
登录 后发表回答