Retrieve X and Y coordinates of a key in android

2.3k views Asked by At

I am using the android's custom keyboard to build a keypad. The OnKeyboardActionListener in android returns the "keyCode" of the key being pressed. But I want in particular, the onscreen x and y coordinates of that key ( with or without using the keyCode ).

P.S : Even coordinates of the center of the key would help. I have found a method "squaredDistanceFrom" which returns the squared distance between the center of the key and point where the screen is touched. Is there a function which returns the center's coordinates?

1

There are 1 answers

0
Alexis Le Compte On

I know this question is quite old now, but I have been working on a project over the past two weeks with similar requirements and I struggled finding a solution. So here is what I propose:

Build a working keyboard

First, let start from a functional keyboard. Some very useful resources to get started:

Get the coordinates of the key pressed

I haven't found a direct way of retrieving the coordinates from the key pressed in the API.

However, we can determine and retrieve the actual instance the key pressed. First we need to create an onTouchListener on the KeyboardView. This will allow us to retrieve a MotionEvent and consequently the coordinates of the touch event within the view.

Then, there is a method in the Keyboard class which is called getKeys returning a list of Keys. We can then use the isInside method from the Key class to verify that the coordinates retrieved previously are inside of the key or not, and thereby retrieving the instance of the key pressed.

We need to retrieve the list of keys from the current keyboard every time is is displayed. This way, even if the user swaps from one layout to another (by using symbols or numbers for instance), we get the correct set of keys. We can do so by overriding the onStartInputView method from our class.

Sample

Retrieve the keys:

public void retrieveKeys() {
    keyList = kv.getKeyboard().getKeys();
}

Override the onStartInputView which is called when the keyboard is displayed:

@Override
public void onStartInputView(EditorInfo info, boolean restarting) {
    super.onStartInputView(info, restarting);
    retrieveKeys();
}

Create an onTouchListener for the KeyboardView:

@Override
public View onCreateInputView() {
    kv = (KeyboardView)getLayoutInflater().inflate(R.layout.keyboard, null);
    kv.setOnKeyboardActionListener(this);
    keyboard = new Keyboard(this, R.xml.qwerty);
    kv.setKeyboard(keyboard);

    retrieveKeys();

    // Set the onTouchListener to be able to retrieve a MotionEvent
    kv.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // For each key in the key list
            for (Keyboard.Key k : keyList) {
                // If the coordinates from the Motion event are inside of the key
                if (k.isInside((int) event.getX(), (int) event.getY())) {
                    // k is the key pressed
                    Log.d("Debugging",
                            "Key pressed: X=" + k.x + " - Y=" + k.y);
                    int centreX, centreY;
                    centreX = (k.width/2) + k.x;
                    centreY = (k.width/2) + k.x;
                    // These values are relative to the Keyboard View
                    Log.d("Debugging",
                            "Centre of the key pressed: X="+centreX+" - Y="+centreY);
                }
            }
            // Return false to avoid consuming the touch event
            return false;
        }
    });

    return kv;
}

Entire example (without the package and imports):

public class MyTestKeyboard extends InputMethodService
        implements KeyboardView.OnKeyboardActionListener {

    private KeyboardView kv;
    private Keyboard keyboard;
    private boolean caps = false;

    private List<Keyboard.Key> keyList;

    public void retrieveKeys() {
        keyList = kv.getKeyboard().getKeys();
    }

    @Override
    public void onStartInputView(EditorInfo info, boolean restarting) {
        super.onStartInputView(info, restarting);
        retrieveKeys();
    }

    @Override
    public View onCreateInputView() {
        kv = (KeyboardView)getLayoutInflater().inflate(R.layout.keyboard, null);
        kv.setOnKeyboardActionListener(this);
        keyboard = new Keyboard(this, R.xml.qwerty);
        kv.setKeyboard(keyboard);

        retrieveKeys();

        // Set the onTouchListener to be able to retrieve a MotionEvent
        kv.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // For each key in the key list
                for (Keyboard.Key k : keyList) {
                    // If the coordinates from the Motion event are inside of the key
                    if (k.isInside((int) event.getX(), (int) event.getY())) {
                        // k is the key pressed
                        Log.d("Debugging",
                                "Key pressed: X=" + k.x + " - Y=" + k.y);
                        int centreX, centreY;
                        centreX = (k.width/2) + k.x;
                        centreY = (k.width/2) + k.x;
                        // These values are relative to the Keyboard View
                        Log.d("Debugging",
                                "Centre of the key pressed: X="+centreX+" - Y="+centreY);
                    }
                }
                // Return false to avoid consuming the touch event
                return false;
            }
        });

        return kv;
    }

    @Override
    public void onKey(int primaryCode, int[] keyCodes) {
        InputConnection ic = getCurrentInputConnection();
        switch(primaryCode){
            case Keyboard.KEYCODE_DELETE :
                ic.deleteSurroundingText(1, 0);
                break;
            case Keyboard.KEYCODE_SHIFT:
                caps = !caps;
                keyboard.setShifted(caps);
                kv.invalidateAllKeys();
                break;
            case Keyboard.KEYCODE_DONE:
                ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
                break;
            default:
                char code = (char)primaryCode;
                if(Character.isLetter(code) && caps){
                    code = Character.toUpperCase(code);
                }
                ic.commitText(String.valueOf(code),1);
        }
    }

    @Override
    public void onPress(int primaryCode) {}

    @Override
    public void onRelease(int primaryCode) {}

    @Override
    public void onText(CharSequence text) {}

    @Override
    public void swipeLeft() {}

    @Override
    public void swipeRight() {}

    @Override
    public void swipeDown() {}

    @Override
    public void swipeUp() {}
}

Note: Tested on a Motorola Moto G XT1039 Android 5.1 API 22