Sunday, February 20, 2011

Android Activity Custom Keyboard

Original Question

The following code demonstrates how to add a custom keyboard to an activity without having to install a third-party keyboard app.

Edit: I have created a project on Google Code which makes the following code obsolete. Please check it out.

Instructions:
  1. add the following files to your project
  2. copy the necessary drawables from \android\platforms\<platform>\data\res\ to your project
  3. profit?

/res/layout/demo.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent" android:layout_height="fill_parent">
 <Button android:id="@+id/button" android:background="@drawable/sym_keyboard_done"
  android:layout_height="fill_parent" android:layout_width="fill_parent" />
 <LinearLayout android:layout_height="wrap_content"
  android:layout_width="wrap_content">
  <android.inputmethodservice.KeyboardView
   android:id="@+id/keyboardView" android:visibility="gone"
   android:focusable="true" android:focusableInTouchMode="true"
   android:layout_height="wrap_content" android:layout_width="wrap_content"
   android:layout_weight="0" />
 </LinearLayout>
</FrameLayout>

/res/xml/qwerty.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
/** 
 *
 * Copyright 2008, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
-->

<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="10%p"
    android:horizontalGap="0px"
    android:verticalGap="0px">

    <Row>
        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
        <Key android:codes="119" android:keyLabel="w"/>
        <Key android:codes="101" android:keyLabel="e"/>
        <Key android:codes="114" android:keyLabel="r"/>
        <Key android:codes="116" android:keyLabel="t"/>
        <Key android:codes="121" android:keyLabel="y"/>
        <Key android:codes="117" android:keyLabel="u"/>
        <Key android:codes="105" android:keyLabel="i"/>
        <Key android:codes="111" android:keyLabel="o"/>
        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p" 
                android:keyEdgeFlags="left"/>
        <Key android:codes="115" android:keyLabel="s"/>
        <Key android:codes="100" android:keyLabel="d"/>
        <Key android:codes="102" android:keyLabel="f"/>
        <Key android:codes="103" android:keyLabel="g"/>
        <Key android:codes="104" android:keyLabel="h"/>
        <Key android:codes="106" android:keyLabel="j"/>
        <Key android:codes="107" android:keyLabel="k"/>
        <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
    </Row>

    <Row>
        <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" 
                android:keyWidth="15%p" android:isModifier="true"
                android:isSticky="true" android:keyEdgeFlags="left"/>
        <Key android:codes="122" android:keyLabel="z"/>
        <Key android:codes="120" android:keyLabel="x"/>
        <Key android:codes="99" android:keyLabel="c"/>
        <Key android:codes="118" android:keyLabel="v"/>
        <Key android:codes="98" android:keyLabel="b"/>
        <Key android:codes="110" android:keyLabel="n"/>
        <Key android:codes="109" android:keyLabel="m"/>
        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" 
                android:keyWidth="15%p" android:keyEdgeFlags="right"
                android:isRepeatable="true"/>
    </Row>

    <Row android:rowEdgeFlags="bottom">
        <Key android:codes="-3" android:keyIcon="@drawable/sym_keyboard_done" 
                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
        <Key android:codes="-2" android:keyLabel="123" android:keyWidth="15%p"/>
        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" 
                android:keyWidth="30%p" android:isRepeatable="true"/>
        <Key android:codes="46,44" android:keyLabel=". ,"
                android:keyWidth="15%p"/>
        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" 
                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
    </Row>
</Keyboard>

public class DemoActivity extends Activity implements OnKeyListener,
  OnKeyboardActionListener {

 private static final String TAG = DemoActivity.class.getName();

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.demo);

  KeyboardView keyboardView = (KeyboardView) findViewById(R.id.keyboardView);
  Keyboard keyboard = new Keyboard(this, R.xml.qwerty);
  keyboardView.setKeyboard(keyboard);
  keyboardView.setEnabled(true);
  keyboardView.setPreviewEnabled(true);
  keyboardView.setOnKeyListener(this);
  keyboardView.setOnKeyboardActionListener(this);

  View button = findViewById(R.id.button);
  button.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    toggleKeyboardVisibility();
   }
  });
 }

 private void toggleKeyboardVisibility() {
  KeyboardView keyboardView = (KeyboardView) findViewById(R.id.keyboardView);
  int visibility = keyboardView.getVisibility();
  switch (visibility) {
   case View.VISIBLE:
    keyboardView.setVisibility(View.INVISIBLE);
    break;
   case View.GONE:
   case View.INVISIBLE:
    keyboardView.setVisibility(View.VISIBLE);
    break;
  }
 }

 @Override
 public boolean onKey(View v, int keyCode, KeyEvent event) {
  Log.d(TAG, "onKey? keyCode=" + keyCode);
  return false;
 }

 @Override
 public void swipeUp() {
  Log.d(TAG, "swipeUp");
 }

 @Override
 public void swipeRight() {
  Log.d(TAG, "swipeRight");
 }

 @Override
 public void swipeLeft() {
  Log.d(TAG, "swipeLeft");
 }

 @Override
 public void swipeDown() {
  Log.d(TAG, "swipeDown");
 }

 @Override
 public void onText(CharSequence text) {
  Log.d(TAG, "onText? \"" + text + "\"");
 }

 @Override
 public void onRelease(int primaryCode) {
  Log.d(TAG, "onRelease? primaryCode=" + primaryCode);
 }

 @Override
 public void onPress(int primaryCode) {
  Log.d(TAG, "onPress? primaryCode=" + primaryCode);
 }

 @Override
 public void onKey(int primaryCode, int[] keyCodes) {
  Log.d(TAG, "onKey? primaryCode=" + primaryCode);
  int n1 = 0; // -1 count
  for (int keyCode : keyCodes) {
   if (keyCode == -1) {
    n1++;
    continue;
   }
   Log.v(TAG, "keyCode=" + keyCode);
  }
  Log.v(TAG, "keyCode=-1 *" + n1);
 }

}

24 comments:

  1. 12th line should be
    Keyboard keyboard = new Keyboard(this, R.xml.qwerty);

    ReplyDelete
  2. Thanks a lot for a wonderful example

    ReplyDelete
  3. I spent a good minute comparing the two lines trying to spot the difference. I can't believe I misspelled "QWERTY". Thanks for letting me know.

    ReplyDelete
  4. Hi. Thanks for posting..
    When does the onText(CharSequence text) called?

    ReplyDelete
  5. Hi, I would like to custom a key on android keyboar. How can I do?
    ex: I want to change text key "Send" to "Submit", how can i do?
    Thanks!

    ReplyDelete
  6. I don't think it is possible to customize an installed keyboard. If you are using the code demonstrated above, it should be as simple as changing android:keyLabel to "Submit"

    ReplyDelete
  7. Ok, Thanks.
    Can you help to how to use cusomize keyboay above in edittext.

    When this keyboard show when touch edittext but it can't put text when press any key on this keyboard.

    ReplyDelete
  8. Hi, Have you tried changing the size, specifically the height, of the keyboard? I've been looking all over but not able to figure how to do this?

    ReplyDelete
  9. By size, are you referring to the number of rows, or to the pixel height of the keyboard?

    ReplyDelete
  10. Thank you for the speedy response. I meant the pixel height of the keyboard. I'm building one for a specific device and would like to lock the overall keyboard height. Appreciate any insights you can provide.

    ReplyDelete
  11. One can artificially control the height of the keyboard by setting the default key height and restricting the number of rows. I am afraid that I do not know of a way to explicitly set the dimensions of the keyboard. I hope this helps.

    ReplyDelete
  12. Hi,
    i used this code snippet and everything works. Thanks. One problem though. The small preview window that appears when a key is pressed on the soft keyboard is blank - it does not show the character pressed, instead a blank squarish window above the key that is pressed.
    Has someone seen this problem with this code snippet ?
    thanks

    ReplyDelete
    Replies
    1. I haven't reviewed this code in almost 2 years, so it is likely out of date. There may have been a change in one of the many recent releases of android. Which version are you testing with?

      Delete
    2. Thanks for the quick reply. i am testing on the android emulator with API level 16 and min api level 11.
      thanks

      Delete
    3. i should clarify that the version is Android 4.1
      thanks

      Delete
    4. I'll test it when I get home and see if I can confirm and fix the bug.

      Delete
    5. @TaG, Thank you very much.

      Delete
    6. Have still been struggling with this. One more datapoint: The key preview window is actually showing the character in a very light color on a white background, which makes it almost invisible.
      If i add a android:keyPreviewLayout="@layout/some_preview_layout" attr for the KeyboardView, it causes a crash in the 'showKey' method of KeyboardView.java. i have submitted a separate question for that in stackoverflow.com
      Any help would be appreciated. If i find something, i will post here.
      thanks

      Delete
    7. I apologize. I forgot to test this when I came home that night. I am currently out of town with only a tablet for internet access. I will take a look on Tuesday. Would you please include a full link to the question on SO so I can monitor the issue?

      Delete
    8. Here is the link to the question on SO related to the crash
      http://stackoverflow.com/questions/16031438/setting-keypreviewlayout-for-android-virtual-keyboard-is-causing-a-crash

      thanks

      Delete
    9. I created a project on Google Code:
      https://code.google.com/p/android-keyboard-demo/

      If you like, I can add you as a collaborator. We can create a branch and try and find a workaround.

      Delete
    10. @Tag, Sorry since i didn't see a response for 5 days, i did not check this page over the last few days. Just seeing your response from Apr 27. Yes, would be happy to collaborate to find a workaround.
      In the meanwhile, i got a workaround for the key press preview problem with a different set of code, but still have problems with the crash with the setting of keyPreviewLayout. Once you create the branch, i can put in the workaround code, but that will not help understand why your code has the key preview problem.
      Will be glad to help in any way.

      Delete