How to create an Android Custom Keyboard Application

How to create an Android Custom Keyboard Application

In this android tutorial, we are going to learn how to create android custom keyboard. If you have ever created an android application like quiz application and you feel like you the application needed a custom keyboard to go with then this tutorial is for you.

They might be a few ways to achieve this in android.

1. creating a view control that extends the KeyboardView and override the onDraw() method of this class.

2. create a xml folder in your project and create a keyboard xml layout resource file for the keys you want to include.

In this application, we are going to accomplish this with the second option and we are going to use a few keyboard keys.

Before we start, it is important to understanding what we plan to create. If you want to read more about android Keyboard Class and KeyboardView, follow this link to android developers user guide.

Android Custom Keyboard

We are going to create an xml layout file in the xml folder we have created in the root directory of our android project. This is the Keyboard layout file we will use. This layout file will be passed as a parameter to the instance of the Keyboard object in our Activity class.

To build a meaning interface, I will add some EditText and TextView controls in our project activity file.

Before we start, it is important for you to understand the tools and environment I used in this application development tutorial. Feel free to use any tools you are familiar with.

Windows 7

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 22

To create a new android application project, following the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidCustomKeyboard

Package: com.inducesmile.androidcustomkeyboard

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

If you are using Android Studio as your choice IDE, the new project will create a default

MainActivity.java file and its corresponding layout file. Open the two files because we are going to

make use of them.

We will create a new file called keyboards.xml layout file in our project xml folder. Copy and paste the code below to this file. You can expand on this if you will like to use more button. Note that you can change the Key width, height, horizontal and vertical gaps between buttons.

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="50%p" android:horizontalGap="1px"
    android:verticalGap="1px" android:keyHeight="40dip">
    <Row>
        <Key android:codes="8" android:keyLabel="1" android:keyEdgeFlags="left" />
        <Key android:codes="9" android:keyLabel="2" />
        <Key android:codes="10" android:keyLabel="3" />
        <Key android:codes="11" android:keyLabel="4" android:keyEdgeFlags="right" />
    </Row>
    <Row>
        <Key android:codes="12" android:keyLabel="5" android:keyEdgeFlags="left" />
        <Key android:codes="13" android:keyLabel="6" />
        <Key android:codes="14" android:keyLabel="7" />
        <Key android:codes="15" android:keyLabel="8" android:keyEdgeFlags="right" />
    </Row>
    <Row>
        <Key android:codes="16" android:keyLabel="9" android:keyEdgeFlags="left" />
        <Key android:codes="7" android:keyLabel="0" />
        <Key android:codes="158" android:keyLabel="." />
        <Key android:codes="66" android:keyEdgeFlags="right" android:keyIcon="@drawable/sym_keyboard_feedback_return" />
    </Row>
    <Row>
        <Key android:codes="67" android:keyIcon="@drawable/sym_keyboard_delete" android:iconPreview="@drawable/sym_keyboard_delete" android:keyEdgeFlags="left" />
        <Key android:codes="161" android:keyLabel="=" android:keyEdgeFlags="right" />
    </Row>
</Keyboard>

You can check the android Keyboard documentation that find information about all the android keyboard key codes here

Open your activity_main.xml layout file. We are going to add our View controls. As I stated before, we are going to make use of 4 different Views – 2 EditText, a TextView and KeyboardView.

Copy and paste the following code inside this layout file.

<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <LinearLayout 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"
        android:orientation="vertical">
        <android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="56dp"
            android:background="@color/primary"
            android:minHeight="?attr/actionBarSize"
            app:titleTextAppearance="@style/AppTheme.Toolbar.Title" />
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <ScrollView
                android:id="@+id/scroll_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_above="@+id/keyboard_view"
                android:layout_marginBottom="8dp"
                android:background="#ffffff"
                android:padding="16dp"
                android:scrollbars="none">
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical">
                    <!--Content of the ScrollView-->
                    <TextView
                        android:id="@+id/before_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="4dp"
                        android:textSize="15dp"
                        android:text="@string/before_value"
                        android:textColor="@color/primary_text"
                        android:textStyle="bold" />
                    <EditText
                        android:id="@+id/before_input"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="4dp"
                        android:background="@drawable/bottom_border"
                        android:inputType="numberDecimal" />
                    <TextView
                        android:id="@+id/percent_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="8dp"
                        android:textSize="15dp"
                        android:text="@string/percent_value"
                        android:textColor="@color/primary_text"
                        android:textStyle="bold" />
                    <EditText
                        android:id="@+id/percentage_input"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="4dp"
                        android:background="@drawable/bottom_border"
                        android:inputType="numberDecimal" />
                    <TextView
                        android:id="@+id/after_label"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="8dp"
                        android:textSize="15dp"
                        android:text="@string/after_value"
                        android:textColor="@color/primary_text"
                        android:textStyle="bold" />
                    <TextView
                        android:id="@+id/percent_result"
                        android:layout_height="wrap_content"
                        android:layout_width="match_parent"
                        android:textColor="@color/primary_text"
                        android:text="@string/no_text"
                        android:padding="16dp"
                        android:textSize="18dp"
                        android:layout_weight="1"
                        android:background="@color/primary_light"/>
                </LinearLayout>
            </ScrollView>
            <android.inputmethodservice.KeyboardView
                android:id="@+id/keyboard_view"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_above="@+id/relativeAd"
                android:layout_centerHorizontal="true"
                android:focusable="true"
                android:background="@android:color/transparent"
                android:keyBackground="@color/primary"
                android:focusableInTouchMode="true"
                android:visibility="gone" />
            <LinearLayout
                android:id="@+id/relativeAd"
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:layout_alignParentBottom="true"
                android:layout_centerHorizontal="true"
                android:background="@color/divider"
                android:gravity="center"
                android:orientation="vertical">
                
            </LinearLayout>
        </RelativeLayout>
    </LinearLayout>
</merge>

You will find out that I used the absolute path of the KeyboardView in the layout file as against using only KeyboardView. I do not understood why it does not work in some phones.

We will head over to our activity file and get the instances of our View controls. We will focus more on the KeyboardView instance. Create a new instance of the KeyboardView by call the findViewById method of the activity class.

We will create an instance of the Keyboard class and pass the application context and the xml layout file we created before.

Finally, we will call the setKeyboard method of the KeyboardView and pass the instance of the Keyboard object as parameter.

import android.app.Activity;
import android.inputmethodservice.KeyboardView;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
public abstract class BasePercentActivity extends AppCompatActivity {

private EditText beforeEditText;
private EditText percentEditText;
private TextView beforeTitle;
private TextView percentTitle;
private TextView afterTitle;

    
protected KeyboardView keyboardView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutResourceId());
        
        beforeTitle = (TextView)findViewById(R.id.before_label);
        percentTitle = (TextView)findViewById(R.id.percent_label);
        afterTitle = (TextView)findViewById(R.id.after_label);
        
        percentResult = (TextView)findViewById(R.id.percent_result);
        
        keyboardView = (KeyboardView)findViewById(R.id.keyboard_view);
        keyboardView.setPreviewEnabled(false);
        keyboard = new Keyboard(PercentDiscountActivity.this, R.xml.keyboards);
        keyboardView.setKeyboard(keyboard);
        keyboardView.setOnKeyboardActionListener(keyboardActionListener);
      
        registerEditText(R.id.before_input);
        registerEditText(R.id.percentage_input);

   getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
    }
    public KeyboardView.OnKeyboardActionListener keyboardActionListener = new KeyboardView.OnKeyboardActionListener() {
        @Override
        public void onPress(int primaryCode) { }
        @Override
        public void onRelease(int primaryCode) { }
        @Override
        public void onKey(int primaryCode, int[] keyCodes) {
            long eventTime = System.currentTimeMillis();
            KeyEvent event = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, primaryCode, 0, 0, 0, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
            dispatchKeyEvent(event);
            if(primaryCode == KeyEvent.KEYCODE_NUMPAD_EQUALS){
                displayCalculatedResult();
            }
        }
        @Override
        public void onText(CharSequence text) { }
        @Override
        public void swipeLeft() { }
        @Override
        public void swipeRight() { }
        @Override
        public void swipeDown() { }
        @Override
        public void swipeUp() { }
    };
    protected abstract int getLayoutResourceId();
    protected abstract void displayCalculatedResult();
    public void registerEditText(int resid) {
        // Find the EditText 'res_id'
        EditText edittext = (EditText) findViewById(resid);
        edittext.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }
            @Override
            public void afterTextChanged(Editable editable) {
                CharSequence mS = editable.subSequence(0, editable.length());
                if (!mS.toString().equals("") || mS.toString() != null) {
                    if (editable.length() > 0 && mS.toString().contains("=")) {
                        editable.replace(editable.length() - 1, editable.length(), "");
                    }
                }
            }
        });
        // Make the custom keyboard appear
        edittext.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) showCustomKeyboard(v);
                else hideCustomKeyboard();
            }
        });
        edittext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showCustomKeyboard(v);
            }
        });
        edittext.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                EditText edittext = (EditText) v;
                int inType = edittext.getInputType();       // Backup the input type
                edittext.setInputType(InputType.TYPE_NULL); // Disable standard keyboard
                edittext.onTouchEvent(event);               // Call native handler
                edittext.setInputType(inType);              // Restore input type
                return true; // Consume touch event
            }
        });
    }
    public void hideCustomKeyboard() {
        keyboardView.setVisibility(View.GONE);
        keyboardView.setEnabled(false);
    }
    public void showCustomKeyboard( View v) {
        keyboardView.setVisibility(View.VISIBLE);
        keyboardView.setEnabled(true);
        if( v!=null ){
            ((InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(v.getWindowToken(), 0);
        }
    }
    public boolean isCustomKeyboardVisible() {
        return keyboardView.getVisibility() == View.VISIBLE;
    }
    @Override public void onBackPressed() {
        if( isCustomKeyboardVisible() ) hideCustomKeyboard(); else this.finish();
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_base_percent, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    public String removeTrailingZero(String formattingInput){
        if(!formattingInput.contains(".")){
            return formattingInput;
        }
        int dotPosition = formattingInput.indexOf(".");
        String newValue = formattingInput.substring(dotPosition, formattingInput.length());
        if(newValue.startsWith(".0")){
            return formattingInput.substring(0, dotPosition);
        }
        return formattingInput;
    }
}

The code includes some other methods we will talk about. We need to hide the default android keyboard so that we can use our own custom keyboard. Another thing we will consider is ability to hide our custom keyboard when the back-press button of android device is press and we will show the custom keyboard when an EditText is onfocus.

This brings us to the end of this tutorial, If you find anything confusing kindly contact me with your questions or use the comment box below.

Now, when you run your application you will see the interface that looks similar to the sample that was shown earlier on.

You can download the code for this tutorial below. If you are having hard time downloading the tutorials, kindly contact me.

Remember to subscribe with your email so that you will be among the first to receive our new post once it is published.

If you like this tutorial, you can see the Scientific Calculator in action in my Complete Mathematics App in Google Play Store. You are free to use and modify the source code for your own use.

12 Comments

    • Inducesmile
    • Inducesmile

Add a Comment