Creating a SoftKeyboard with Multiple/Alternate characters per key

27,324

Solution 1

Implementing alternate key popup:

For each key you wish to have a popup keyboard you should define popupCharacters and popupKeyboard:

/res/xml/[Keyboard].xml

<Key android:keyLabel="("
    android:popupKeyboard="@xml/keyboard_popup_template"
    android:popupCharacters="[{&lt;" />

The popupKeyboard is an XML representation of the keyboard used in the popup containing the alternate keys:

/res/xml/keyboard_popup_template.xml

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

Styling the alternate key popup:

If you want to change the layout/style of the popup (which defaults to @android:layout/ keyboard_popup_keyboard.xml) you can specify a android:popupLayout attribute which points to a layout file:

<android.inputmethodservice.KeyboardView
    android:id="@+id/keyboard"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:background="#FF272727"
    android:popupLayout="@layout/keyboard_popup_keyboard" />

Implementing Key Preview Overlay:

The only solution I've been able to knock together to show key previews (Without entirely rewriting the KeyboardView source code) is below:

Wrapping the <KeyboardView> tag with a <FrameLayout> with a height specified by multiplying the keyHeight by the amount of rows. Inside this tag I've simply created a LinearLayout to hold rows, then a LinearLayout for each row containing a TextView with a weight equal to the %p value specified for each <Key>:

<TextView android:text="!" style="@style/Custom.Widget.KeyboardKeyOverlay"  android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="10"/>

And styled:

<style name="CustomTheme.Widget.KeyboardKeyOverlay">
    <item name="android:background">@android:color/transparent</item>
    <item name="android:textColor">#FFAAAAAA</item>
    <item name="android:paddingRight">6dp</item>
    <item name="android:paddingTop">4dp</item>
    <item name="android:textSize">10sp</item>
    <item name="android:gravity">right</item>
    <item name="android:textStyle">bold</item>
</style>         

Which produces this:

enter image description here

I won't be happy until I've managed to implement this in the same way as the System Keyboard does!

Solution 2

Judging from my own attempt at coding a softkeyboard I found out that:

  • Nice/bling features usually requires that you extend KeyboardView and basically write large parts of the drawing code. Unfortunately you cannot do this by overriding some key methods since almost everything is private. You might want to take a look (and borrow some code from:
    • (base)/core/java/android/inputmethodservice/KeyboardView.java (android core code repo)
    • (apps)/other/LatinIME/LatinKeyboardView.java (android core apps repo)

Note that the sheep on android.kernel.org is there to tell you that the repo is closed due to crackers but there are mirrors of the code elsewhere (lost the links unfortunately)

  • The base KeyboardView has no support for shadowed key hints, you must code your own KeyboardView to get a chance to override the onDraw() method.

Now on what you can do:

  • You can workaround this issue by providing pictures for the keys: use xml <Key ... android:keyIcon="@drawable/this_key_icon_file /> for this. Unfortunately, you'll most certainly have poor results for letters with this method (resolution issues).

  • You can use (and configure appearance of) popup keyboard that appears on long press.

Declare a keyboard template res/xml/kbd_popup_template.xml :

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

Declare string values containing the keys you want on this keyboard res/values/strings.xml:

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <string name="alternates_for_a">àáâãäåæ</string>
</ressources>

Then, use both in your keyboard layout definition:

<Key android:codes="97" android:keyLabel="a"  
    android:popupKeyboard="@xml/kbd_popup_template"
    android:popupCharacters="@string/alternates_for_a" />
  • You can also use double-tap, triple-tap, ... feature to generate alternates for the key you're tapping. To do so, simply use a list for the android keycodes:

    <Key android:codes="97,224,230" .../>

will produce 97='a' for single tap, 224='à' for double-tap and 230='æ' for triple-tap.

The duration to consider double-tapping is set to 800ms in android source code. It's unfortunately hardcoded (and a bit high, I feel).

Be aware that, when double-tapping, it basically sends an 'a' first, then, on the second tap it sends 'à'. Some apps, will not like this.

Solution 3

That popup keyboard with the close button is annoying when we have only one popup character. Simpler way is to override the onLongPress method of KeyboardView class like this.

@Override
protected boolean onLongPress(Key key) {
    if (key.codes[0] == '1') {
        getOnKeyboardActionListener().onKey('!', null);
        return true;
    }
}

Solution 4

If you want to have a text on top of your key, you can do it in onDraw() method in your class that overrides KeyboardView

 @Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    ...
    Paint paint = new Paint();
    paint.setTextAlign(Paint.Align.CENTER);
    paint.setTextSize(18);
    paint.setColor(Color.WHITE);
    //get all your keys and draw whatever you want
    List <Keyboard.Key> keys = getKeyboard().getKeys();
    for(Keyboard.Key key: keys) {
        if(key.label != null) {

            if (key.label.toString().equals("q") || key.label.toString().equals("Q"))
                canvas.drawText(String.valueOf(1), key.x + (key.width / 2) + 10, key.y + 25, paint);

            else if (key.label.toString().equals("w") || key.label.toString().equals("W"))
                canvas.drawText(String.valueOf(2), key.x + (key.width / 2) + 10, key.y + 25, paint);

            else if (key.label.toString().equals("e") || key.label.toString().equals("E"))
                canvas.drawText(String.valueOf(3), key.x + (key.width / 2) + 10, key.y + 25, paint);

            else if (key.label.toString().equals("r") || key.label.toString().equals("R"))
                canvas.drawText(String.valueOf(4), key.x + (key.width / 2) + 10, key.y + 25, paint);

            else if (key.label.toString().equals("t") || key.label.toString().equals("T"))
                canvas.drawText(String.valueOf(5), key.x + (key.width / 2) + 10, key.y + 25, paint);

            else if (key.label.toString().equals("y") || key.label.toString().equals("Y"))
                canvas.drawText(String.valueOf(6), key.x + (key.width / 2) + 10, key.y + 25, paint);

            else if (key.label.toString().equals("u") || key.label.toString().equals("U"))
                canvas.drawText(String.valueOf(7), key.x + (key.width / 2) + 10, key.y + 25, paint);

            else if (key.label.toString().equals("i") || key.label.toString().equals("I"))
                canvas.drawText(String.valueOf(8), key.x + (key.width / 2) + 10, key.y + 25, paint);

            else if (key.label.toString().equals("o") || key.label.toString().equals("o"))
                canvas.drawText(String.valueOf(9), key.x + (key.width / 2) + 10, key.y + 25, paint);

            else if (key.label.toString().equals("p") || key.label.toString().equals("P"))
                canvas.drawText(String.valueOf(0), key.x + (key.width / 2) + 10, key.y + 25, paint);

            else
            {}
        }
    }
}

Solution 5

For anyone trying to dismiss the popup keyboard by tapping outside its view area, I've had some luck putting a TouchListener on the KeyboardView inside the class extending InputMethodService

public class YourIME extends InputMethodService{
    @Override 
    public View onCreateInputView() {
        mInputView = (LatinKeyboardView) getLayoutInflater().inflate(R.layout.input, null);
        setLatinKeyboard(mQwertyKeyboard);

        mInputView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {

                if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) {                        
                    mInputView.closing(); // Close popup keyboard if it's showing
                }
                return false;
            }
        });

        return mInputView;
    }
// The rest of your ime ...
}
Share:
27,324

Related videos on Youtube

Graeme
Author by

Graeme

Java Enterprise / Android Developer

Updated on July 09, 2022

Comments

  • Graeme
    Graeme almost 2 years

    I've followed the examples on developer.android.com regarding Input Methods and played with the SoftKeyboard sample application. These together give more than enough information regarding the creation of simple keyboard.

    What I can't see in the API is the ability to create alternate / multiple characters per key which is available on the standard Keyboard (LatinIME Keyboard).

    enter image description here

    The above image is the result of a long press on the "a" key. When you long press a key it's possible to populate a popup with alternate characters.

    enter image description here

    It is also possible to give a popup hint on some keys which will prompt the user to press and hold a key in order to get the popup menu.

    So far I haven't found a single source of information on how this is achieved, hopefully someone will be able to give me a head start, until then I'll follow the source code of the inbuilt keyboard and see if I can reverse engineer it.

    Edit: Would help if developer.android.com 's link to the LatinIME Keyboard didn't link to a picture of a Sheep :) Actual source code for LatinIME.java.

    Edit 2: More as a reference than anything else, this is the sequence I believe a usual longPress action goes through in order to show the popup keyboard in KeyboardView.java:

    onTouchEvent()
    onModifiedTouchEvent()
    mHandkler.handleMessage() with MSG_LONGPRESS
    openPopupIfRequired() 
    onLongPress()
    

    Edit 3:

    I still haven't figured this out - How do you add label suggestions to keys? An answer suggests it isn't built into the API and indeed I haven't found the codeto do this. However the Keyboard on 2.3.4 (API 10) shows this functionality being implemented:

    enter image description here

    Would very much like to figure out how IT does it but it isn't anywhere in the onDraw() method that I can see - which makes me believe it's being written outside of the KeyboardView element. I can't however find the layout file used to display the KeyboardView element on the inbuilt keyboard - If anyone knows where to find this perhaps that will give me the clue I need.

    Edit 4: Moved key Preview question here as it's slightly off topic:

    How do you disable the SoftKeyboard key preview window?

    • Mateen Chaudhry
      Mateen Chaudhry about 6 years
      Did you find to solution to implement custom popups? please update
  • Ashwin N Bhanushali
    Ashwin N Bhanushali about 12 years
    @Graeme, I didn't understood implementing key preview overlay. Do I include keyboardView within Framelayout? If Yes then How would keyboard rows have to be declared? Please can you guide me show that I can develop descent IME. Thanks.
  • IgorGanapolsky
    IgorGanapolsky almost 12 years
    Unfortunately when I follow this solution, there is an 'x' (close button) that appears with the popup characters. And it won't go away until I select it or a character. How can I avoid getting this 'x' button??
  • Laurent'
    Laurent' almost 12 years
    Hi Igor G., unfortunately I can not help you there: this doesn't happen to me neither on Froyo nor ICS. Maybe is this device/vendor dependant?
  • IgorGanapolsky
    IgorGanapolsky almost 12 years
    Thank you. But I have tried with different devices, and different OS versions. So maybe it's just my code.
  • José Silva
    José Silva about 9 years
    @Graeme, can you provide the sources to your solution? Nice workaround, btw!
  • ilomambo
    ilomambo about 8 years
    Just an improvement suggestion over multiple if-else: use switch(key.codes[0]) { ... } statement to speed up the code.
  • Fedor Tsyganov
    Fedor Tsyganov about 8 years
    Yeah there are multiple ways to improve this code, for instance use equalsIgnoreCase() etc, the point is to show that you can draw that text programmatically. Anyway thanks for your suggestion.
  • Sammy T
    Sammy T over 7 years
    Is there a way to replace the default close button on the popup keyboard? I know you can change the layout but I'm trying to create a way for the user to close the popup keyboard by touching somewhere outside of its area.
  • ChRoNoN
    ChRoNoN over 7 years
    There is just a problem here, if you click outside of the popup on the keyboard it will output the letter you clicked, need to add a condition to if so it only happens when the popup is open
  • Ian
    Ian over 6 years
    In addition to this answer the undocumented method KeyboardView.handleBack() dismisses the popup keyboard grepcode.com/file/repository.grepcode.com/java/ext/… This can be called from onTouchEvent() in a class extended from KeyboardView when ACTION_UP occurs
  • Mateen Chaudhry
    Mateen Chaudhry about 6 years
    isn't is expensive on lower devices because onDraw call twice when press any key from keyboard and release it?
  • user1510006
    user1510006 almost 6 years
    may you add complete solution please, I missed up with overriding the custom keyboard for the multiple keys key.
  • Martin Zaske
    Martin Zaske over 5 years
    Great answer. Sadly this link is dead: @android:layout/keyboard_popup_keyboard.xml) You are showing how to call for a custom popup-layout; could you please add an example of a custom styling file. I need nothing fancy, am just trying to give my popups the same background colour and frame-colour as my main (custom) keyboard. Thank you.
  • zsd
    zsd over 4 years
    The working link for @android:layout/keyboard_popup_keyboard.xml: android.googlesource.com/platform/frameworks/base/+/master/c‌​ore/…
  • Matthew Morrone
    Matthew Morrone over 3 years
    I have this issue but this doesn't seem to do anything. Do setOnTouchListener and setOnKeyboardActionListener somehow collide, or...?