How can you make a custom keyboard in Android? [closed]
Asked Answered
L

9

148

I want to make a custom keyboard. I don't know how to do it using XML and Java. The following picture is a model of the keyboard I want to make. It only needs numbers.

enter image description here

Laban answered 6/3, 2012 at 2:57 Comment(3)
[Create Your Own Custom Keyboard using XML Layouts for Android Devices ](tutorials-android.blogspot.com/2011/06/…)Serviceberry
There is a good tutorial at Tuts : linkEm
Google has a sample "SoftKeyboard" project, or there are quite a lot of resources linked here: customkeyboarddetails.blogspot.com/2019/02/…Micropaleontology
C
133

#System keyboard

This answer tells how to make a custom system keyboard that can be used in any app that a user has installed on their phone. If you want to make a keyboard that will only be used within your own app, then see my other answer.

The example below will look like this. You can modify it for any keyboard layout.

enter image description here

The following steps show how to create a working custom system keyboard. As much as possible I tried to remove any unnecessary code. If there are other features that you need, I provided links to more help at the end.

#1. Start a new Android project

I named my project "Custom Keyboard". Call it whatever you want. There is nothing else special here. I will just leave the MainActivity and "Hello World!" layout as it is.

#2. Add the layout files

Add the following two files to your app's res/layout folder:

  • keyboard_view.xml
  • key_preview.xml

keyboard_view.xml

This view is like a container that will hold our keyboard. In this example there is only one keyboard, but you could add other keyboards and swap them in and out of this KeyboardView.

<?xml version="1.0" encoding="utf-8"?>
<android.inputmethodservice.KeyboardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/keyboard_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:keyPreviewLayout="@layout/key_preview"
    android:layout_alignParentBottom="true">

</android.inputmethodservice.KeyboardView>

key_preview.xml

The key preview is a layout that pops up when you press a keyboard key. It just shows what key you are pressing (in case your big, fat fingers are covering it). This isn't a multiple choice popup. For that you should check out the Candidates view.

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:background="@android:color/white"
    android:textColor="@android:color/black"
    android:textSize="30sp">
</TextView>

#3. Add supporting xml files

Create an xml folder in your res folder. (Right click res and choose New > Directory.)

Then add the following two xml files to it. (Right click the xml folder and choose New > XML resource file.)

  • number_pad.xml
  • method.xml

number_pad.xml

This is where it starts to get more interesting. This Keyboard defines the layout of the keys.

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="20%p"
    android:horizontalGap="5dp"
    android:verticalGap="5dp"
    android:keyHeight="60dp">

    <Row>
        <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/>
        <Key android:codes="50" android:keyLabel="2"/>
        <Key android:codes="51" android:keyLabel="3"/>
        <Key android:codes="52" android:keyLabel="4"/>
        <Key android:codes="53" android:keyLabel="5" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:codes="54" android:keyLabel="6" android:keyEdgeFlags="left"/>
        <Key android:codes="55" android:keyLabel="7"/>
        <Key android:codes="56" android:keyLabel="8"/>
        <Key android:codes="57" android:keyLabel="9"/>
        <Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:codes="-5"
             android:keyLabel="DELETE"
             android:keyWidth="40%p"
             android:keyEdgeFlags="left"
             android:isRepeatable="true"/>
        <Key android:codes="10"
             android:keyLabel="ENTER"
             android:keyWidth="60%p"
             android:keyEdgeFlags="right"/>
    </Row>

</Keyboard>

Here are some things to note:

  • keyWidth: This is the default width of each key. The 20%p means that each key should take up 20% of the width of the parent. It can be overridden by individual keys, though, as you can see happened with the Delete and Enter keys in the third row.
  • keyHeight: It is hard coded here, but you could use something like @dimen/key_height to set it dynamically for different screen sizes.
  • Gap: The horizontal and vertical gap tells how much space to leave between keys. Even if you set it to 0px there is still a small gap.
  • codes: This can be a Unicode or custom code value that determines what happens or what is input when the key is pressed. See keyOutputText if you want to input a longer Unicode string.
  • keyLabel: This is the text that is displayed on the key.
  • keyEdgeFlags: This indicates which edge the key should be aligned to.
  • isRepeatable: If you hold down the key it will keep repeating the input.

method.xml

This file tells the system the input method subtypes that are available. I am just including a minimal version here.

<?xml version="1.0" encoding="utf-8"?>
<input-method
    xmlns:android="http://schemas.android.com/apk/res/android">

    <subtype
        android:imeSubtypeMode="keyboard"/>

</input-method>

#4. Add the Java code to handle key input

Create a new Java file. Let's call it MyInputMethodService. This file ties everything together. It handles input received from the keyboard and sends it on to whatever view is receiving it (an EditText, for example).

import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.view.inputmethod.InputConnection;
import android.view.View;
import android.text.TextUtils;

public class MyInputMethodService extends InputMethodService implements KeyboardView.OnKeyboardActionListener {

    @Override
    public View onCreateInputView() {
        // get the KeyboardView and add our Keyboard layout to it
        KeyboardView keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.keyboard_view, null);
        Keyboard keyboard = new Keyboard(this, R.xml.number_pad);
        keyboardView.setKeyboard(keyboard);
        keyboardView.setOnKeyboardActionListener(this);
        return keyboardView;
    }

    @Override
    public void onKey(int primaryCode, int[] keyCodes) {

        InputConnection ic = getCurrentInputConnection();
        if (ic == null) return;
        switch (primaryCode) {
            case Keyboard.KEYCODE_DELETE:
                CharSequence selectedText = ic.getSelectedText(0);
                if (TextUtils.isEmpty(selectedText)) {
                    // no selection, so delete previous character
                    ic.deleteSurroundingText(1, 0);
                } else {
                    // delete the selection
                    ic.commitText("", 1);
                }
                break;
            default:
                char code = (char) primaryCode;
                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() { }
}

Notes:

  • The OnKeyboardActionListener listens for keyboard input. It is also requires all those empty methods in this example.
  • The InputConnection is what is used to send input to another view like an EditText.

#5. Update the manifest

I put this last rather than first because it refers to the files we already added above. To register your custom keyboard as a system keyboard, you need to add a service section to your AndroidManifest.xml file. Put it in the application section after activity.

<manifest ...>
    <application ... >
        <activity ... >
            ...
        </activity>

        <service
            android:name=".MyInputMethodService"
            android:label="Keyboard Display Name"
            android:permission="android.permission.BIND_INPUT_METHOD">
            <intent-filter>
                <action android:name="android.view.InputMethod"/>
            </intent-filter>
            <meta-data
                android:name="android.view.im"
                android:resource="@xml/method"/>
        </service>

    </application>
</manifest>

That's it! You should be able to run your app now. However, you won't see much until you enable your keyboard in the settings.

#6. Enable the keyboard in Settings

Every user who wants to use your keyboard will have to enable it in the Android settings. For detailed instructions on how to do that, see the following link:

Here is a summary:

  • Go to Android Settings > Languages and input > Current keyboard > Choose keyboards.
  • You should see your Custom Keyboard on the list. Enable it.
  • Go back and choose Current keyboard again. You should see your Custom Keyboard on the list. Choose it.

Now you should be able to use your keyboard anywhere that you can type in Android.

#Further study

The keyboard above is usable, but to create a keyboard that other people will want to use you will probably have to add more functionality. Study the links below to learn how.

#Going On

Don't like how the standard KeyboardView looks and behaves? I certainly don't. It looks like it hasn't been updated since Android 2.0. How about all those custom keyboards in the Play Store? They don't look anything like the ugly keyboard above.

The good news is that you can completely customize your own keyboard's look and behavior. You will need to do the following things:

  1. Create your own custom keyboard view that subclasses ViewGroup. You could fill it with Buttons or even make your own custom key views that subclass View. If you use popup views, then note this.
  2. Add a custom event listener interface in your keyboard. Call its methods for things like onKeyClicked(String text) or onBackspace().
  3. You don't need to add the keyboard_view.xml, key_preview.xml, or number_pad.xml described in the directions above since these are all for the standard KeyboardView. You will handle all these UI aspects in your custom view.
  4. In your MyInputMethodService class, implement the custom keyboard listener that you defined in your keyboard class. This is in place of KeyboardView.OnKeyboardActionListener, which is no longer needed.
  5. In your MyInputMethodService class's onCreateInputView() method, create and return an instance of your custom keyboard. Don't forget to set the keyboard's custom listener to this.
Counterwork answered 6/7, 2017 at 4:27 Comment(0)
S
93

First of all you will need a keyboard.xml file which will be placed in the res/xml folder (if the folder does not exist, created it).

<?xml version="1.0" encoding="utf-8"?> 
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="15%p"
    android:keyHeight="15%p" >

    <Row>
        <Key android:codes="1"    android:keyLabel="1" android:horizontalGap="4%p"/>
        <Key android:codes="2"    android:keyLabel="2" android:horizontalGap="4%p"/>
        <Key android:codes="3"    android:keyLabel="3" android:horizontalGap="4%p" />
        <Key android:codes="4"    android:keyLabel="4" android:horizontalGap="4%p" />
        <Key android:codes="5"    android:keyLabel="5" android:horizontalGap="4%p" />
    </Row>
    <Row>
        <Key android:codes="6"    android:keyLabel="6" android:horizontalGap="4%p"/>
        <Key android:codes="7"    android:keyLabel="7" android:horizontalGap="4%p"/>
        <Key android:codes="8"    android:keyLabel="8" android:horizontalGap="4%p" />
        <Key android:codes="9"    android:keyLabel="9" android:horizontalGap="4%p" />
        <Key android:codes="0"    android:keyLabel="0" android:horizontalGap="4%p" />
    </Row>

    <Row>
        <Key android:codes="-1"    android:keyIcon="@drawable/backspace" android:keyWidth="34%p" android:horizontalGap="4%p"/>
        <Key android:codes="100"    android:keyLabel="Enter" android:keyWidth="53%p" android:horizontalGap="4%p"/>
    </Row>
 </Keyboard>

**Note that you will have to create the backspace drawable and place it in the res/drawable-ldpi folder with a very small size (like 18x18 pixels)

Then in the xml file that you want it to be used (where your TextView is in) you should add the following code:

<RelativeLayout
 ...
>

        .....


        <android.inputmethodservice.KeyboardView
             android:id="@+id/keyboardview"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_alignParentBottom="true"
             android:layout_centerHorizontal="true"
             android:focusable="true"
             android:focusableInTouchMode="true"
             android:visibility="gone" 
         />
         
        ......


</RelativeLayout>

**Note that the xml file that you will place the android.inputmethodservice.KeyboardView in, has to be RelativeLayout in order to be able to set the alignParentBottom="true" (Usually the keyboards are presented in the bottom of the screen)

Then you need to add the following code in the onCreate function of the Activity that handles the TextView you want to attach the keyboard to

    // Create the Keyboard
    mKeyboard= new Keyboard(this,R.xml.keyboard);

    // Lookup the KeyboardView
    mKeyboardView= (KeyboardView)findViewById(R.id.keyboardview);
    // Attach the keyboard to the view
    mKeyboardView.setKeyboard( mKeyboard );
    
    // Do not show the preview balloons
    //mKeyboardView.setPreviewEnabled(false);
    
    // Install the key handler
    mKeyboardView.setOnKeyboardActionListener(mOnKeyboardActionListener);

**Note that mKeyboard and mKeyboardView are private class variables that you have to create.

Then you need the following function for opening the keyboard ( you must associate it with the TextView through the onClick xml property)

    public void openKeyboard(View v)
    {
       mKeyboardView.setVisibility(View.VISIBLE);
       mKeyboardView.setEnabled(true);
       if( v!=null)((InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(v.getWindowToken(), 0);
    }

And finally you need the OnKeyboardActionListener that will handle your events

private OnKeyboardActionListener mOnKeyboardActionListener = new OnKeyboardActionListener() {
    @Override public void onKey(int primaryCode, int[] keyCodes) 
    {
         //Here check the primaryCode to see which key is pressed 
         //based on the android:codes property
         if(primaryCode==1)
         {
            Log.i("Key","You just pressed 1 button");
         }
    }

    @Override public void onPress(int arg0) {
    }

    @Override public void onRelease(int primaryCode) {
    }

    @Override public void onText(CharSequence text) {
    }

    @Override public void swipeDown() {
    }

    @Override public void swipeLeft() {
    }

    @Override public void swipeRight() {
    }

    @Override public void swipeUp() {
    }
};

Hope it helps!

Most of the code found here

____________________________________________________________-

EDIT:

Since KeyboardView is depreciated since API level 29, you can find its code in this website and create a class in your code before implementing the keyboard as described above.

Seychelles answered 27/7, 2014 at 9:50 Comment(6)
What if I don't want the keyboard to be on the bottom of the screen? (e.g. I want the user to be able to drag it around). Is that something I can control via my keyboard app or is it handled by the android system?Tiffanytiffi
the keyboard width is not filling the screen what should i do to make it fill in all screensEating
what is the parent layout that the KeyboardView is in? Also have you checked the layout_width of the KeyboardView??Seychelles
Please be aware that KeyboardView and Keyboard classes are deprecated by Google since API level 29. So this solution will not work anymore in future if you have to target a newer API level.Parados
keyboardView is deprecated by google. what is new solution?Procora
How can we display recyclerview inside custom keyboard layout instead of Keys?Durno
C
57

In-App Keyboard

This answer tells how to make a custom keyboard to use exclusively within your app. If you want to make a system keyboard that can be used in any app, then see my other answer.

The example will look like this. You can modify it for any keyboard layout.

enter image description here

1. Start a new Android project

I named my project InAppKeyboard. Call yours whatever you want.

2. Add the layout files

Keyboard layout

Add a layout file to res/layout folder. I called mine keyboard. The keyboard will be a custom compound view that we will inflate from this xml layout file. You can use whatever layout you like to arrange the keys, but I am using a LinearLayout. Note the merge tags.

res/layout/keyboard.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/button_1"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="1"/>

            <Button
                android:id="@+id/button_2"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="2"/>

            <Button
                android:id="@+id/button_3"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="3"/>

            <Button
                android:id="@+id/button_4"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="4"/>

            <Button
                android:id="@+id/button_5"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="5"/>

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/button_6"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="6"/>

            <Button
                android:id="@+id/button_7"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="7"/>

            <Button
                android:id="@+id/button_8"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="8"/>

            <Button
                android:id="@+id/button_9"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="9"/>

            <Button
                android:id="@+id/button_0"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="0"/>

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/button_delete"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="2"
                android:text="Delete"/>

            <Button
                android:id="@+id/button_enter"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="3"
                android:text="Enter"/>

        </LinearLayout>
    </LinearLayout>

</merge>

Activity layout

For demonstration purposes our activity has a single EditText and the keyboard is at the bottom. I called my custom keyboard view MyKeyboard. (We will add this code soon so ignore the error for now.) The benefit of putting all of our keyboard code into a single view is that it makes it easy to reuse in another activity or app.

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.inappkeyboard.MainActivity">

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#c9c9f1"
        android:layout_margin="50dp"
        android:padding="5dp"
        android:layout_alignParentTop="true"/>

    <com.example.inappkeyboard.MyKeyboard
        android:id="@+id/keyboard"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_alignParentBottom="true"/>

</RelativeLayout>

3. Add the Keyboard Java file

Add a new Java file. I called mine MyKeyboard.

The most important thing to note here is that there is no hard link to any EditText or Activity. This makes it easy to plug it into any app or activity that needs it. This custom keyboard view also uses an InputConnection, which mimics the way a system keyboard communicates with an EditText. This is how we avoid the hard links.

MyKeyboard is a compound view that inflates the view layout we defined above.

MyKeyboard.java

public class MyKeyboard extends LinearLayout implements View.OnClickListener {

    // constructors
    public MyKeyboard(Context context) {
        this(context, null, 0);
    }

    public MyKeyboard(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyKeyboard(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    // keyboard keys (buttons)
    private Button mButton1;
    private Button mButton2;
    private Button mButton3;
    private Button mButton4;
    private Button mButton5;
    private Button mButton6;
    private Button mButton7;
    private Button mButton8;
    private Button mButton9;
    private Button mButton0;
    private Button mButtonDelete;
    private Button mButtonEnter;

    // This will map the button resource id to the String value that we want to 
    // input when that button is clicked.
    SparseArray<String> keyValues = new SparseArray<>();

    // Our communication link to the EditText
    InputConnection inputConnection;

    private void init(Context context, AttributeSet attrs) {

        // initialize buttons
        LayoutInflater.from(context).inflate(R.layout.keyboard, this, true);
        mButton1 = (Button) findViewById(R.id.button_1);
        mButton2 = (Button) findViewById(R.id.button_2);
        mButton3 = (Button) findViewById(R.id.button_3);
        mButton4 = (Button) findViewById(R.id.button_4);
        mButton5 = (Button) findViewById(R.id.button_5);
        mButton6 = (Button) findViewById(R.id.button_6);
        mButton7 = (Button) findViewById(R.id.button_7);
        mButton8 = (Button) findViewById(R.id.button_8);
        mButton9 = (Button) findViewById(R.id.button_9);
        mButton0 = (Button) findViewById(R.id.button_0);
        mButtonDelete = (Button) findViewById(R.id.button_delete);
        mButtonEnter = (Button) findViewById(R.id.button_enter);

        // set button click listeners
        mButton1.setOnClickListener(this);
        mButton2.setOnClickListener(this);
        mButton3.setOnClickListener(this);
        mButton4.setOnClickListener(this);
        mButton5.setOnClickListener(this);
        mButton6.setOnClickListener(this);
        mButton7.setOnClickListener(this);
        mButton8.setOnClickListener(this);
        mButton9.setOnClickListener(this);
        mButton0.setOnClickListener(this);
        mButtonDelete.setOnClickListener(this);
        mButtonEnter.setOnClickListener(this);

        // map buttons IDs to input strings
        keyValues.put(R.id.button_1, "1");
        keyValues.put(R.id.button_2, "2");
        keyValues.put(R.id.button_3, "3");
        keyValues.put(R.id.button_4, "4");
        keyValues.put(R.id.button_5, "5");
        keyValues.put(R.id.button_6, "6");
        keyValues.put(R.id.button_7, "7");
        keyValues.put(R.id.button_8, "8");
        keyValues.put(R.id.button_9, "9");
        keyValues.put(R.id.button_0, "0");
        keyValues.put(R.id.button_enter, "\n");
    }

    @Override
    public void onClick(View v) {

        // do nothing if the InputConnection has not been set yet
        if (inputConnection == null) return;

        // Delete text or input key value
        // All communication goes through the InputConnection
        if (v.getId() == R.id.button_delete) {
            CharSequence selectedText = inputConnection.getSelectedText(0);
            if (TextUtils.isEmpty(selectedText)) {
                // no selection, so delete previous character
                inputConnection.deleteSurroundingText(1, 0);
            } else {
                // delete the selection
                inputConnection.commitText("", 1);
            }
        } else {
            String value = keyValues.get(v.getId());
            inputConnection.commitText(value, 1);
        }
    }

    // The activity (or some parent or controller) must give us 
    // a reference to the current EditText's InputConnection
    public void setInputConnection(InputConnection ic) {
        this.inputConnection = ic;
    }
}

4. Point the keyboard to the EditText

For system keyboards, Android uses an InputMethodManager to point the keyboard to the focused EditText. In this example, the activity will take its place by providing the link from the EditText to our custom keyboard to.

Since we aren't using the system keyboard, we need to disable it to keep it from popping up when we touch the EditText. Second, we need to get the InputConnection from the EditText and give it to our keyboard.

MainActivity.java

public class MainActivity extends AppCompatActivity {

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

        EditText editText = (EditText) findViewById(R.id.editText);
        MyKeyboard keyboard = (MyKeyboard) findViewById(R.id.keyboard);

        // prevent system keyboard from appearing when EditText is tapped
        editText.setRawInputType(InputType.TYPE_CLASS_TEXT);
        editText.setTextIsSelectable(true);

        // pass the InputConnection from the EditText to the keyboard
        InputConnection ic = editText.onCreateInputConnection(new EditorInfo());
        keyboard.setInputConnection(ic);
    }
}

If your Activity has multiple EditTexts, then you will need to write code to pass the right EditText's InputConnection to the keyboard. (You can do this by adding an OnFocusChangeListener and OnClickListener to the EditTexts. See this article for a discussion of that.) You may also want to hide or show your keyboard at appropriate times.

Finished

That's it. You should be able to run the example app now and input or delete text as desired. Your next step is to modify everything to fit your own needs. For example, in some of my keyboards I've used TextViews rather than Buttons because it is easier to customize them.

Notes

  • In the xml layout file, you could also use a TextView rather a Button if you want to make the keys look better. Then just make the background be a drawable that changes the appearance state when pressed.
  • Advanced custom keyboards: For more flexibility in keyboard appearance and keyboard switching, I am now making custom key views that subclass View and custom keyboards that subclass ViewGroup. The keyboard lays out all the keys programmatically. The keys use an interface to communicate with the keyboard (similar to how fragments communicate with an activity). This is not necessary if you only need a single keyboard layout since the xml layout works fine for that. But if you want to see an example of what I have been working on, check out all the Key* and Keyboard* classes here. Note that I also use a container view there whose function it is to swap keyboards in and out.
Counterwork answered 10/7, 2017 at 7:6 Comment(18)
your answer is great, but how can we set toggling between an original keyboard and this new keyboard.Felicity
@KishanDonga, On your keyboard you can add a key to switch keyboards. When the user presses it call InputMethodManager#showInputMethodPicker(). If the original keyboard does not have such a key, though, the only way users can switch to your keyboard is to do it manually in the system settings. Apple is superior to Android in this area, because Apple requires all keyboards to have a keyboard switching key.Counterwork
@KishanDonga, I just realized that this answer is about an in-app keyboard, not the system keyboard. If you want to swap between two custom keyboards, then you can programmatically swap them in and out of a container view. Just add a swap keyboards key on both keyboards. See my "advanced custom keyboards" note and link in the answer above.Counterwork
If you want to swap between your keyboard and the system keyboard, then hide the system keyboard and show your keyboard at the appropriate times (and vice versa).Counterwork
Great example, but is there a way how to make the custom keyboard a default one for all inputs across all the screens?Bagby
@MarekTakac, you will need to disable the system keyboard and add your custom keyboard in every activity. If an activity has multiple EditTexts then you will need to add an onFocusChangedListener to them so that when they receive focus you can assign the InputConnection from the current EditText to your custom keyboard.Counterwork
Thanks for the reply @Suragch. That's unfortunately not an option for me because most of the app I am working on is written in React Native and not Java and if I understand it correctly I would have to rewrite all React Native screens that need to use my custom keyboard into Java. I guess my only option then is to go with system keyboard. Thanks again.Bagby
@Counterwork How can I achieve "adjustPan"Cotyledon
@LalitJadav, you could put the custom keyboard in a layout that sits on top of the activity layout. Then hide or animate the keyboard layout as needed.Counterwork
Great answers, I had to create one on Kotlin and this helped me, how do you animate the keyboard?Brittnee
@Brittnee You could do something like this: https://mcmap.net/q/86819/-show-and-hide-a-view-with-a-slide-up-down-animationCounterwork
@ How to do you get click listener from custom keyboard in MainActivity? Basically I want to play TTS on click of a particular key on custom keyboard after typing the message in EditText.Mcquoid
"prevent system keyboard from appearing when EditText is tapped" is not working, the system keyboard still pops up. editText.setShowSoftInputOnFocus(false); and now worksMarishamariska
Yeah, it works with showSoftInputOnFocus = false. Please update your answer. I'm using API 26.Cot
@CarlosLeonardoCamiloVargas, I'm not currently working with native Android much now, but if you've tested the code you can update my answer.Counterwork
@Counterwork This will work when we try with Edittext, what are we want to type EditText which is inside the Recyclerviews Cardview? how can we setup up this? because the adapter class will contain Edittext property, How can we set it up please answer this?Lai
@Prasath, sorry, I'm not sure.Counterwork
Thanks for the answer. If you (or anyone) would update this answer for kotlin and KeyboardView being deprecated it would be even more helpful.Bruch
I
31

Use KeyboardView:

KeyboardView kbd = new KeyboardView(context);
kbd.setKeyboard(new Keyboard(this, R.xml.custom));

kbd.setOnKeyboardActionListener(new OnKeyboardActionListener() {
    ....
}

now you have kbd which is a normal view.

The nice thing about this is that R.xml.custom refers to /res/xml/custom.xml, which defines in xml the layout of the keyboard. For more information on this file, look here: Keyboard, Keyboard.Row, Keyboard.Key.

Impignorate answered 12/8, 2012 at 16:20 Comment(1)
I am using KeyboardView class, but as of API 29, it is now deprecated.Photina
L
14

Here is a sample project for a soft keyboard.

https://developer.android.com/guide/topics/text/creating-input-method.html

Your's should be in the same lines with a different layout.

Edit: If you need the keyboard only in your application, its very simple! Create a linear layout with vertical orientation, and create 3 linear layouts inside it with horizontal orientation. Then place the buttons of each row in each of those horizontal linear layouts, and assign the weight property to the buttons. Use android:layout_weight=1 for all of them, so they get equally spaced.

This will solve. If you didn't get what was expected, please post the code here, and we are here to help you!

Lavoie answered 6/3, 2012 at 3:11 Comment(1)
The edit is actually bad because that would mean the keyboard is always shown and will not behave like stock Android keyboard.Subadar
J
7

One of the best well-documented example I found.

http://www.fampennings.nl/maarten/android/09keyboard/index.htm

KeyboardView related XML file and source code are provided.

Jenjena answered 9/2, 2014 at 8:30 Comment(1)
KeyboardView is deprecated nowWaldman
B
6

I came across this post recently when I was trying to decide what method to use to create my own custom keyboard. I found the Android system API to be very limited, so I decided to make my own in-app keyboard. Using Suragch's answer as the basis for my research, I went on to design my own keyboard component. It's posted on GitHub with an MIT license. Hopefully this will save somebody else a lot of time and headache.

The architecture is pretty flexible. There is one main view (CustomKeyboardView) that you can inject with whatever keyboard layout and controller you want.

You just have to declare the CustomKeyboardView in you activity xml (you can do it programmatically as well):

    <com.donbrody.customkeyboard.components.keyboard.CustomKeyboardView
    android:id="@+id/customKeyboardView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true" />

Then register your EditText's with it and tell it what type of keyboard they should use:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val numberField: EditText = findViewById(R.id.testNumberField)
    val numberDecimalField: EditText = findViewById(R.id.testNumberDecimalField)
    val qwertyField: EditText = findViewById(R.id.testQwertyField)

    keyboard = findViewById(R.id.customKeyboardView)
    keyboard.registerEditText(CustomKeyboardView.KeyboardType.NUMBER, numberField)
    keyboard.registerEditText(CustomKeyboardView.KeyboardType.NUMBER_DECIMAL, numberDecimalField)
    keyboard.registerEditText(CustomKeyboardView.KeyboardType.QWERTY, qwertyField)
}

The CustomKeyboardView handles the rest!

I've got the ball rolling with a Number, NumberDecimal, and QWERTY keyboard. Feel free to download it and create your own layouts and controllers. It looks like this:

android custom keyboard gif landscape

enter image description here

Even if this is not the architecture you decide to go with, hopefully it'll be helpful to see the source code for a working in-app keyboard.

Again, here's the link to the project: Custom In-App Keyboard

EDIT: I'm no longer an Android developer, and I no longer maintain this GitHub project. There are probably more modern approaches and architectures at this point, but please feel free to reference the GitHub project if you'd like and fork it.

Biparous answered 23/7, 2018 at 2:7 Comment(0)
E
3

Well Suragch gave the best answer so far but he skipped certain minor stuff that was important to getting the app compiled.

I hope to make a better answer than Suragch by improving on his answer. I will add all the missing elements he didnt put.

I compiled my apk using the android app , APK Builder 1.1.0. So let's begin.

To build an Android app we need couple files and folders that are organized in a certain format and capitalized accordingly.

res layout -> xml files depicting how app will look on phone. Similar to how html shapes how web page looks on browser. Allowing your app to fit on screens accordingly.

values -> constant data such as colors.xml, strings.xml, styles.xml. These files must be properly spelt.

drawable -> pics{jpeg, png,...}; Name them anything.

mipmap -> more pics. used for app icon?

xml -> more xml files.

src -> acts like JavaScript in html. layout files will initiate the starting view and your java file will dynamically control the tag elements and trigger events. Events can also be activated directly in the layout.xml just like in html.

AndroidManifest.xml -> This file registers what your app is about. Application name, Type of program, permissions needed, etc. This seems to make Android rather safe. Programs literally cannot do what they didnt ask for in the Manifest.

Now there are 4 types of Android programs, an activity, a service, a content provider, and a broadcast reciever. Our keyboard will be a service, which allows it to run in the background. It will not appear in the list of apps to launch; but it can be uninstalled.

To compile your app, involves gradle, and apk signing. You can research that one or use APK Builder for android. It is super easy.

Now that we understand Android development, let us create the files and folders.

  1. Create the files and folders as I discussed above. My directory wil look as follows:

    • NumPad
      • AndroidManifest.xml
      • src
        • Saragch
          • num_pad
            • MyInputMethodService.java
      • res
        • drawable
          • Suragch_NumPad_icon.png
        • layout
          • key_preview.xml
          • keyboard_view.xml
        • xml
          • method.xml
          • number_pad.xml
        • values
          • colors.xml
          • strings.xml
          • styles.xml

Remember if you are using an ide such as Android Studio it may have a project file.

  1. Write files.

A: NumPad/res/layout/key_preview.xml

<?xml version="1.0" encoding="utf-8"?>
   <TextView
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:gravity="center"
      android:background="@android:color/white"
      android:textColor="@android:color/black"
      android:textSize="30sp">
</TextView>

B: NumPad/res/layout/keyboard_view.xml

<?xml version="1.0" encoding="utf-8"?>
<android.inputmethodservice.KeyboardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/keyboard_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:keyPreviewLayout="@layout/key_preview"
    android:layout_alignParentBottom="true">

</android.inputmethodservice.KeyboardView>

C: NumPad/res/xml/method.xml

<?xml version="1.0" encoding="utf-8"?>
<input-method  xmlns:android="http://schemas.android.com/apk/res/android">
    <subtype  android:imeSubtypeMode="keyboard"/>
</input-method>

D: Numpad/res/xml/number_pad.xml

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="20%p"
    android:horizontalGap="5dp"
    android:verticalGap="5dp"
    android:keyHeight="60dp">

    <Row>
        <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/>
        <Key android:codes="50" android:keyLabel="2"/>
        <Key android:codes="51" android:keyLabel="3"/>
        <Key android:codes="52" android:keyLabel="4"/>
        <Key android:codes="53" android:keyLabel="5" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:codes="54" android:keyLabel="6" android:keyEdgeFlags="left"/>
        <Key android:codes="55" android:keyLabel="7"/>
        <Key android:codes="56" android:keyLabel="8"/>
        <Key android:codes="57" android:keyLabel="9"/>
        <Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:codes="-5"
             android:keyLabel="DELETE"
             android:keyWidth="40%p"
             android:keyEdgeFlags="left"
             android:isRepeatable="true"/>
        <Key android:codes="10"
             android:keyLabel="ENTER"
             android:keyWidth="60%p"
             android:keyEdgeFlags="right"/>
    </Row>

</Keyboard>

Of course this can be easily edited to your liking. You can even use images instead lf words for the label.

Suragch didnt demonstrate the files in the values folder and assumed we had access to Android Studio; which automatically creates them. Good thing I have APK Builder.

E: NumPad/res/values/colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
</resources>

F: NumPad/res/values/strings.xml

<resources>
    <string name="app_name">Suragch NumPad</string>
</resources>

G: NumPad/res/values/styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
        <!-- Customize your theme here. -->
    </style>

</resources>

H: Numpad/AndroidManifest.xml

This is the file that was really up for contension. Here I felt I would never compile my program. sob. sob. If you check Suracgh's answer you see he leaves the first set of fields empty, and adds the activity tag in this file. As I said there are four types of Android programs. An activity is a regular app with a launcher icon. This numpad is not an activity! Further he didnt implement any activity.

My friends do not include the activity tag. Your program will compile, and when you try to launch it will crash! As for xmlns:android and uses-sdk; I cant help you there. Just try my settings if they work.

As you can see there is a service tag, which register it as a service. Also service.android:name must be name of public class extending service in our java file. It MUST be capitalized accordingly. Also package is the name of the package we declared in java file.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="Saragch.num_pad">

    <uses-sdk
        android:minSdkVersion="12"
        android:targetSdkVersion="27" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/Suragch_NumPad_icon"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <service
            android:name=".MyInputMethodService"
            android:label="Keyboard Display Name"
            android:permission="android.permission.BIND_INPUT_METHOD">

            <intent-filter>
                <action android:name="android.view.InputMethod"/>
            </intent-filter>

            <meta-data
                android:name="android.view.im"
                android:resource="@xml/method"/>

        </service>

    </application>
</manifest>

I: NumPad/src/Saragch/num_pad/MyInputMethodService.java

Note: I think java is an alternative to src.

This was another problem file but not as contentious as the manifest file. As I know Java good enough to know what is what, what is not. I barely know xml and how it ties in with Android development!

The problem here was he didnt import anything! I mean, he gave us a "complete" file which uses names that couldnt be resolved! InputMethodService, Keyboard, etc. That is bad practice Mr. Suragch. Thanks for helping me out but how did you expect the code to compile if the names cant be resolved?

Following is the correctly edited version. I just happened to pounce upon couple hints to drove me to the right place to learn what exactly to import.

package Saragch.num_pad;

import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.KeyboardView;
import android.inputmethodservice.Keyboard;

import android.text.TextUtils;
import android.view.inputmethod.InputConnection;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;


public class MyInputMethodService extends InputMethodService implements KeyboardView.OnKeyboardActionListener 
{
    @Override
    public View onCreateInputView() 
    {
     // get the KeyboardView and add our Keyboard layout to it
     KeyboardView keyboardView = (KeyboardView)getLayoutInflater().inflate(R.layout.keyboard_view, null);
     Keyboard keyboard = new Keyboard(this, R.xml.number_pad);
     keyboardView.setKeyboard(keyboard);
     keyboardView.setOnKeyboardActionListener(this);
     return keyboardView;
    }

    @Override
    public void onKey(int primaryCode, int[] keyCodes) 
    {

        InputConnection ic = getCurrentInputConnection();

        if (ic == null) return;

        switch (primaryCode)
        {
         case Keyboard.KEYCODE_DELETE:
            CharSequence selectedText = ic.getSelectedText(0);

            if (TextUtils.isEmpty(selectedText)) 
            {
             // no selection, so delete previous character
             ic.deleteSurroundingText(1, 0);
            }

            else 
            {
             // delete the selection
             ic.commitText("", 1);
            }

            ic.deleteSurroundingText(1, 0);
            break;

         default:
            char code = (char) primaryCode;
            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() { }
}
  1. Compile and sign your project.

    This is where I am clueless as a newby Android developer. I would like to learn it manually, as I believe real programmers can compile manually.

I think gradle is one of the tools for compiling and packaging to apk. apk seems to be like a jar file or a rar for zip file. There are then two types of signing. debug key which is not alllowed on play store and private key.

Well lets give Mr. Saragch a hand. And thank you for watching my video. Like, subscribe.

Eustace answered 24/5, 2019 at 9:16 Comment(0)
B
1

Had the same problem. I used table layout at first but the layout kept changing after a button press. Found this page very useful though. http://mobile.tutsplus.com/tutorials/android/android-user-interface-design-creating-a-numeric-keypad-with-gridlayout/

Blunderbuss answered 26/8, 2013 at 9:23 Comment(2)
Not sure where spaghetti is to be found. Example only has like 5 lines of code implementing onCreate.Brickle
Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.Induct

© 2022 - 2024 — McMap. All rights reserved.