Search code examples
android

How to make it possible to switch layouts on a custom keyboard? Android, keyboard development


I'm developing my own android keyboard. I have two xml files which are two different keyboard layouts: "english_lower.xml" and "symbols.xml". I need the user to be able to click on a button that switches layout "english_lower.xml" to a layout "symbols.xml".

File "english_lower.xml":

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="2dp"
    android:verticalGap="2dp"
    android:background="@drawable/keybutton"
    android:keyHeight="48dp"
    android:keyWidth="12%p">

    <Row>
        <Key android:keyLabel="1" android:keyEdgeFlags="left"/>
        <Key android:keyLabel="2"/>
        <Key android:keyLabel="3"/>
        <Key android:keyLabel="4"/>
        <Key android:keyLabel="5"/>
        <Key android:keyLabel="6"/>
        <Key android:keyLabel="7"/>
        <Key android:keyLabel="8"/>
        <Key android:keyLabel="9"/>
        <Key android:keyLabel="0" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:keyLabel="q" android:keyEdgeFlags="left"/>
        <Key android:keyLabel="w"/>
        <Key android:keyLabel="e"/>
        <Key android:keyLabel="r"/>
        <Key android:keyLabel="t"/>
        <Key android:keyLabel="y"/>
        <Key android:keyLabel="u"/>
        <Key android:keyLabel="i"/>
        <Key android:keyLabel="o"/>
        <Key android:keyLabel="p" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:keyLabel="a" android:keyEdgeFlags="left"/>
        <Key android:keyLabel="s"/>
        <Key android:keyLabel="d"/>
        <Key android:keyLabel="f"/>
        <Key android:keyLabel="g"/>
        <Key android:keyLabel="h"/>
        <Key android:keyLabel="j"/>
        <Key android:keyLabel="k"/>
        <Key android:keyLabel="l" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:keyLabel="z"/>
        <Key android:keyLabel="x"/>
        <Key android:keyLabel="c"/>
        <Key android:keyLabel="v"/>
        <Key android:keyLabel="b"/>
        <Key android:keyLabel="n"/>
        <Key android:keyLabel="m"/>
        <Key
            android:codes="-5"
            android:isRepeatable="true"
            android:keyWidth="12%p"
            android:textSize="20dp"
            android:keyLabel="❮" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key
            android:codes="0"
            android:keyLabel="!1#"/>
        <Key android:keyLabel=","/>
        <Key
            android:codes="32"
            android:keyWidth="64%p"
            android:keyLabel="Space"/>
        <Key android:keyLabel="."/>
        <Key
            android:codes="10"
            android:keyEdgeFlags="right"
            android:textSize="20dp"
            android:keyLabel="↵"/>
    </Row>

</Keyboard>

File "symbols.xml":

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="2dp"
    android:verticalGap="2dp"
    android:background="@drawable/keybutton"
    android:keyHeight="48dp"
    android:keyWidth="12%p">

    <Row>
        <Key android:keyLabel="1" android:keyEdgeFlags="left"/>
        <Key android:keyLabel="2"/>
        <Key android:keyLabel="3"/>
        <Key android:keyLabel="4"/>
        <Key android:keyLabel="5"/>
        <Key android:keyLabel="6"/>
        <Key android:keyLabel="7"/>
        <Key android:keyLabel="8"/>
        <Key android:keyLabel="9"/>
        <Key android:keyLabel="0" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:keyLabel="\u0040" android:keyEdgeFlags="left"/>
        <Key android:keyLabel="#"/>
        <Key android:keyLabel="$"/>
        <Key android:keyLabel="_"/>
        <Key android:keyLabel="\u0026"/>
        <Key android:keyLabel="-"/>
        <Key android:keyLabel="+"/>
        <Key android:keyLabel="("/>
        <Key android:keyLabel=")"/>
        <Key android:keyLabel="/" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:keyLabel="*" android:keyEdgeFlags="left"/>
        <Key android:keyLabel="\u0022"/>
        <Key android:keyLabel="'"/>
        <Key android:keyLabel=":"/>
        <Key android:keyLabel=";"/>
        <Key android:keyLabel="!"/>
        <Key android:keyLabel="\u003f" android:keyEdgeFlags="right"/>
        <Key
            android:codes="-5"
            android:isRepeatable="true"
            android:keyWidth="12%p"
            android:textSize="20dp"
            android:keyLabel="❮" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key
            android:codes="0"
            android:keyLabel="ABC"/>
        <Key android:keyLabel=","/>
        <Key
            android:codes="32"
            android:keyWidth="64%p"
            android:keyLabel="Space"/>
        <Key android:keyLabel="."/>
        <Key
            android:codes="10"
            android:keyEdgeFlags="right"
            android:textSize="20dp"
            android:keyLabel="↵"/>
    </Row>

</Keyboard>

File "Custom_keyboard_app.java":

...

public class CustomKeyboardApp extends InputMethodService
        implements KeyboardView.OnKeyboardActionListener {

    int symbols = 1;

    @Override
    public View onCreateInputView() {

        KeyboardView keyboardView;
        Keyboard keyboard;
        keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.custom_keyboard_layout, null);

        keyboard = new Keyboard(this, R.xml.symbols);

        keyboardView.setKeyboard(keyboard);
        keyboardView.invalidateAllKeys();

        keyboardView.setOnKeyboardActionListener(this);
        return keyboardView;
    }

    ...

    @Override
    public void onKey(int primaryCode, int[] keyCodes) {
        KeyboardView keyboardView;
        Keyboard keyboard;
        keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.custom_keyboard_layout, null);

        InputConnection inputConnection = getCurrentInputConnection();

        if (inputConnection != null) {

            switch (primaryCode) {

                case Keyboard.KEYCODE_DELETE:
                    CharSequence selectedText = inputConnection.getSelectedText(0);

                    if (TextUtils.isEmpty(selectedText)) {
                        inputConnection.deleteSurroundingText(1, 0);
                    } else {
                        inputConnection.commitText("", 1);
                    }
                    break;

                case 0:
                    if (symbols == 1) {
                        keyboard = new Keyboard(this, R.xml.custom_keypad);
                        symbols = 2;
                        inputConnection.commitText(" True", 1);
                    } else {
                        keyboard = new Keyboard(this, R.xml.custom_lowers);
                        symbols = 1;
                        inputConnection.commitText(" False", 1);
                    }
                    keyboardView.setKeyboard(keyboard);
                    keyboardView.invalidateAllKeys();
                    keyboardView.setOnKeyboardActionListener(this);
                    break;
                default:
                    char code = (char) primaryCode;
                    inputConnection.commitText(String.valueOf(code), 1);
            }
        }
    }

    ...

}

I tried to solve the problem using

if (symbols == 1) {
    keyboard = new Keyboard(this, R.xml.custom_keypad);
    symbols = 2;
    inputConnection.commitText(" True", 1);
    } else {
    keyboard = new Keyboard(this, R.xml.custom_lowers);
    symbols = 1;
    inputConnection.commitText(" False", 1);
}

, but the layout always remains the same, although the words " True" and " False" are written alternately when the button is clicked.


Solution

  • Seems like you are doing a small mistake. onKey function is inflating the new KeyboardView and keyboard each time a key is pressed.

    You can follow below steps to resolve this :

    1. onCreateInputView and onKey member variables should be declared as variables outside keyboardView and keyboard functions.

    2. Inside onKey function, set value of symbols variable accordingly and update the keyboard object, without creating a new key or keyboard object.


    complete code :

    public class CustomKeyboardApp extends InputMethodService implements KeyboardView.OnKeyboardActionListener {
    
        private int symbols = 1;
        private KeyboardView keyboardView;
        private Keyboard keyboard;
    
        @Override
        public View onCreateInputView() {
            keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.custom_keyboard_layout, null);
    
            keyboard = new Keyboard(this, R.xml.symbols); // Initial keyboard
    
            keyboardView.setKeyboard(keyboard);
            keyboardView.invalidateAllKeys();
    
            keyboardView.setOnKeyboardActionListener(this);
            return keyboardView;
        }
    
        @Override
        public void onKey(int primaryCode, int[] keyCodes) {
    
            if (primaryCode == Keyboard.KEYCODE_DELETE) {
                CharSequence selectedText = getCurrentInputConnection().getSelectedText(0);
    
                if (TextUtils.isEmpty(selectedText)) {
                    getCurrentInputConnection().deleteSurroundingText(1, 0);
                } else {
                    getCurrentInputConnection().commitText("", 1);
                }
                return;
            }
    
            if (primaryCode == 0) {
                if (symbols == 1) {
                    keyboard = new Keyboard(this, R.xml.custom_keypad);
                    symbols = 2;
                } else {
                    keyboard = new Keyboard(this, R.xml.custom_lowers);
                    symbols = 1;
                }
                keyboardView.setKeyboard(keyboard); // Update existing keyboard
                keyboardView.invalidateAllKeys(); // Redraw keys
            }
    
            char code = (char) primaryCode;
            getCurrentInputConnection().commitText(String.valueOf(code), 1);
        }
    }
    

    You can read more details from here :