15

I have a EditText component, and, of course, if you click on it, the Android keypad is shown, allowing the user to input text. As far as I know, all Android software keyboards have (at least) a letter mode (ABC) and a symbols mode (?123). Their default view is the letter mode.

Now when the keypad is shown when the EditText component is clicked, I want the symbols mode to be shown by default. The user will still be able to switch to the letter mode.

enter image description here

Is there a way to achieve that? If yes, how?

OneWorld
  • 17,512
  • 21
  • 86
  • 136
MC Emperor
  • 22,334
  • 15
  • 80
  • 130
  • I started the bounty and my additional requirements are: It's a patient ID field that in 95% of all cases contains numbers but sometimes letters are possible as well. What's the best work around? – OneWorld Mar 21 '15 at 08:40
  • http://stackoverflow.com/questions/4929040/android-start-in-symbol-keyboard-mode-but-dont-restrict-to-numbers-only-input?lq=1 Almost same question. However, there the requirements are input of math equations which really requires symbols not just numbers. – OneWorld Mar 23 '15 at 09:49

5 Answers5

17

I'm posting this because I don't think any of the answers actually address the question. The screenshot in the question does not correspond to a particular InputType's default state. So, switching InputTypes will not give you the layout from the screenshot.

(based on my research...)

Support for symbol input is not governed by any contract. One can very well leave symbols out when creating their own InputMethod. OR, they can add pagination support to provide access to 100s of symbols. Can this be bound by a contract? May be. But, it isn't at present.

Input method framework does not allow direct communication between the client and the IME. All communication happens either through the InputMethodManager or through InputConnection — a one-way channel. Switching to symbols using ?123 is, however, an internal event — not a defined state/action. Client applications cannot switch to it. There's no public (or hidden) API to make this happen.

InputType indicates something entirely different to an IME. Not sure why everyone is recommending its use. You may of course find that a particular InputType provides most of the required keys. But that isn't the same as show[ing] Android keyboard with symbols mode by default.

Possible workaround:

We'll create a custom EditText. We don't have to. It'll just keep everything in one place, and save us from copy-paste nightmare.

public class CusEditText extends EditText {

    private final int mDefinedActionId;

    public CusEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
         
        // Corresponds to 'android:imeActionId' value
        mDefinedActionId = getResources().getInteger(R.integer.definedActionId);

        setOnEditorActionListener(new OnEditorActionListener() {
        
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                Log.i("CusEditText", "onEditorAction, actionId = " + actionId);

                // Only bother if (...)
                if (actionId == mDefinedActionId) {

                    // Check if current InputType is NUMBER
                    if ((getInputType() & InputType.TYPE_CLASS_NUMBER) != 0) {
                        // Toggle
                        setImeActionLabel("NUM", mDefinedActionId);
                        setInputType(InputType.TYPE_CLASS_TEXT);
                    } else {
                        // Current InputType is TEXT // Toggle
                        setImeActionLabel("ABC", mDefinedActionId);
                        setInputType(InputType.TYPE_CLASS_NUMBER);
                    }

                    // We've handled this
                    return true;
                }
            
                // Let someone else worry about this
                return false;
            }
        });
    }
}

Next, we need to define definedActionId. Open or create res/values/integers.xml and add:

<integer name="definedActionId">-100</integer>

-100 is an arbitrary value. I checked EditorInfo and the actionIds (IME_ACTION_XXXX) were >= 0. -100 seems like a good candidate.

In xml, your layout will look like:

<com.your.packagename.CusEditText
    android:layout_width="blah"
    android:layout_height="blah"
    android:inputType="number"
    android:imeActionId="@integer/definedActionId"
    android:imeActionLabel="ABC"/>

<!-- Probably use @string resource in place of ABC -->

There's not much to explain. IME will launch in NUMBER mode. Instead of a checkmark icon, it'll display ABC. On click, we intercept the actionId and toggle between NUMBER and TEXT input. We're using setInputType(...) because it not only updates the InputType, it also restarts the IME with changes. setRawInputType(...) only updates the InputType.

Issues:

As you can tell, this isn't really a solution. If the user closes the keyboard(using the back button) in TEXT mode, the keyboard will remain in the TEXT mode when they open it again. To go to the NUMBER mode, user will have to click NUM. Also, in TEXT mode, user will see NUM as the action, along with ?123 option. This doesn't break anything, but does take away from the UX.

We can't do anything about ?123 showing in TEXT mode for reasons listed above. But, we can try to make sure that the keyboard always opens in the NUMBER mode. I'll provide a rough sketch of how we'll do that. Its not straight-forward since we (developers) are not privy to events such as keyboard closing or opening. Updated CusEditText:

public class CusEditText extends EditText {

    private final int mDefinedActionId;
    private long mLastEditorActionTime = 0L;

    public CusEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
         
        // Corresponds to 'android:imeActionId' value
        mDefinedActionId = getResources().getInteger(R.integer.definedActionId);

        setOnEditorActionListener(new OnEditorActionListener() {
        
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                Log.i("CusEditText", "onEditorAction, actionId = " + actionId);

                // Only bother if (...)
                if (actionId == mDefinedActionId) {

                    // setInputType(...) will restart the IME
                    // and call finishComposingText() 
                    // see below
                    mLastEditorActionTime = SystemClock.elapsedRealtime();

                    // Check if current InputType is NUMBER
                    if ((getInputType() & InputType.TYPE_CLASS_NUMBER) != 0) {
                        // Toggle
                        setImeActionLabel("NUM", mDefinedActionId);
                        setInputType(InputType.TYPE_CLASS_TEXT);
                    } else {
                        // Current InputType is TEXT // Toggle
                        setImeActionLabel("ABC", mDefinedActionId);
                        setInputType(InputType.TYPE_CLASS_NUMBER);
                    }

                    // We've handled this
                    return true;
                }
            
                // Let someone else worry about this
                return false;
            }
        });
    }

    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        InputConnection inputConnection = super.onCreateInputConnection(outAttrs);

        return new CusInputConnectionWrapper(inputConnection, false);
    }

    private class CusInputConnectionWrapper extends InputConnectionWrapper {
        private CusInputConnectionWrapper(InputConnection target, boolean mutable) {
            super(target, mutable);
        }

        @Override
        public boolean finishComposingText() {
            Log.i("CICW", "finishComposingText");

            // Ignore finishComposingText for 1 second (1000L)
            if (SystemClock.elapsedRealtime() - mLastEditorActionTime > 1000L) {
                if ((getInputType() & InputType.TYPE_CLASS_NUMBER) == 0) {
                    // InputConnection is no longer valid.
                    // Switch back to NUMBER iff required
                    setImeActionLabel("ABC", mDefinedActionId);
                    setInputType(InputType.TYPE_CLASS_NUMBER);
                }
            }

            return super.finishComposingText();
        }
    }
}

Again, code is self-explanatory. We create a InputConnectionWrapper and listen for the finishComposingText() callback. If we're manually switching between TEXT and NUMBER, we use a flag since finishComposingText() will automatically be called. Else, we check if input type is set to TEXT and change it to NUMBER. I am not sure if finishComposingText() is the right method for interpreting keyboard closing/opening. Testing on API 21, vanilla android, this seems to work. More tests will be required.

I really hope someone can come up with a better, more robust solution than this - or modify my workaround so that it doesn't look like one.

Summary

Task at hand is to provide functionality of switching between NUMBER & TEXT input modes around existing Input Method Engines (IMEs). The first approach was to use imeActionLabel & imeActionId in the switching mechanism. This approach worked well with Google's keyboard (this is the imeActionLabel), but failed with Samsung's - imeActionLabel failed to show up in portrait (without extract). Possible workaround is to include the toggle button in the app's own UI.

Even with Google's keyboard, the letters (text) fail to show up when the mode switches back to NUMBER after inputting letters. This problem was fixed (at least on tested devices) by using flag flagNoExtractUi which prevents the IME from entering fullscreen mode in landscape orientation.

Final solution (pending implementation & testing)

  • The IME starts in the NUMBER input mode (95% use-cases involve number input)
  • A button is added to app's UI (next to the EditText) for switching between NUMBER & TEXT mode
  • User can switch from NUMBER to TEXT without any restrictions. Switching back from TEXT to NUMBER requires that no alphabets have been added.
  • InputType is preserved between keyboard closing & reopening. Example: If the user switches to TEXT mode and closes the keyboard, it will open in the TEXT mode. The InputType is not reset.

For more information about the approaches tried, refer to this discussion thread.

Screenshots

Default (NUMBER):

enter image description here

Switched to TEXT:

enter image description here

Recorded video link

Community
  • 1
  • 1
Vikram
  • 51,313
  • 11
  • 93
  • 122
  • 1
    At this point I should share my actual requirements: It's a patient ID field that in 95% of all cases contains numbers but sometimes letters are possible as well. What work around is best in this case? First sophisticated answer by the way! Thank you! – OneWorld Mar 20 '15 at 21:32
  • @OneWorld The 95 / 5 distribution makes this frustrating :)) All this for 5%! Well, I have come up with a workaround using a custom editor action. If you are okay with the issues listed above, ditch the second implementation of `CusEditText`(second Java code-block). I am not very confident about the second cos I've only tested it on one device & API level. You can of course give it a shot and see how it performs. – Vikram Mar 21 '15 at 01:05
  • Looks promising so far. Could you pls provide some screenshots of your solution. So that one can easily recognize the workaround with its improvements and flaws without reading all lines of code? – OneWorld Mar 21 '15 at 08:27
  • I see you overwrite the button that is normally used to jump to the next EditText if there are any. One must know that when implementing your solution, right? – OneWorld Mar 21 '15 at 09:12
  • @OneWorld You can still indicate to the IME that you need forward/backward jumps using `android:imeOptions="flagNavigateNext|flagNavigatePrevious"`. In this case, long pressing on the `NUM`/`ABC` button will show previous/next option. – Vikram Mar 21 '15 at 09:18
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/73469/discussion-between-vikram-and-oneworld). – Vikram Mar 21 '15 at 09:19
  • @OneWorld I've added a brief summary of what we had tried, and the direction you'll be heading in from here. I had actually written a fairly long summary before this - firefox crashed and took that along. So, if you feel something's missing, please do let me know. – Vikram Mar 27 '15 at 07:40
  • Great summary. I think we really found a solution for a heavily discussed problem on SO. But it's for other users still not a fast to read answer. My suggestion: Put "Summary" and "Final solution" to front. Followed by one code example and screenshots that match the final solution. Delete the rest including your comments, that were just discussion. I'm deleting my one now. – OneWorld Mar 27 '15 at 08:48
  • 2
    @Vikram the details provided in this answer is way more than what documentation gives. Really amazing how deep you dig to get all these info. Really cool stuff. – Raghunandan Jan 06 '16 at 13:26
  • I made integers.xml file from scratch. Should have been: -100 . Thanks to this https://developer.android.com/samples/SpeedTracker/Wearable/res/values/integers.html – gimmegimme Feb 21 '17 at 17:43
  • Is there a way to get ABC / NUM to take the replace the 'comma' ket, instead of the Enter/return key? I use the enter key as button to trigger the evaluation of the expression entered? – gimmegimme Feb 24 '17 at 17:58
2

I agree it is an InputType. If you want to show only numbers to your user then you would add the following to you xml document for your edit text:

        android:inputType="number"

However if you set it as number then the user has to enter a number. But you can add additional types as well like numbers and email addresses such as:

        android:inputType="number|textEmailAddress"

Check out http://developer.android.com/reference/android/text/InputType.html for more options. You can also check out what eclipse or android studio shows you under "inputType"

Dean
  • 396
  • 3
  • 10
  • 4
    Downvote since the answer isn't that easy. If you register number as inputType, the keyboard gets always locked to Number keyboard (12_KEY) - no matter what additional flag you add to inputType like "textEmailAddress" in your example. – OneWorld Mar 20 '15 at 10:46
0

I believe you are looking to set the InputType of your edit text.

http://developer.android.com/reference/android/text/InputType.html

I'm not sure which you would use though you may have to play around a bit.

Spaceman Spiff
  • 934
  • 1
  • 14
  • 32
0

The only way to do this is by setting the inputType of your EditText.

If you want to set this property in the onCreate() (or inside a custom View's constructor) you can use the method setRawInputType():

mEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);

Otherwise, if you need to set this property after the onCreate() (or after a custom View's constructor), you can use the method setInputType():

mEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);

Obviously you can also specify the property at XML level:

android:inputType="number|numberDecimal"

You can play around with different flags to find the best composed filter.

bonnyz
  • 13,458
  • 5
  • 46
  • 70
  • 1
    Don't see a difference to Deans and Ian's answer which aren't sufficient. – OneWorld Mar 20 '15 at 21:33
  • @OneWorld I think that you cannot do more than this, the input type is the only way to trick the keyboard (my solution use different flags that should match most of your inputs) – bonnyz Mar 20 '15 at 22:03
  • @bonnys My customers wouldn't understand that if I would explain them the issue as you do. A little bit more creativity is necessary here. If Ian's and Dean's answers were sufficient I would not have started the bounty which clearly asks for work arounds if no direct solution is possible. – OneWorld Mar 21 '15 at 08:35
0

Programmatically it is possible with little bit of tweak to the usual flow. First you have to set editText as:

editText.setInputType(InputType.TYPE_CLASS_NUMBER);

Then you have to listen for keyevent. On pressing of pound set the InputType again to InputType.TYPE_CLASS_TEXT. This should work as it works for me.

editText.setOnKeyListener(new View.OnKeyListener() 
        {
         @Override
         public boolean onKey(View v, int keyCode, KeyEvent event) {
                                // TODO Auto-generated method stub
                                Log.d("KeyBoard", "Keyboard Test Key Hit");

         switch (keyCode) {
         KeyEvent.KEYCODE_POUND:
                                                                                      if(editText.setInputType(InputType.TYPE_CLASS_TEXT);
         {

         editText.setInputType(InputType.TYPE_CLASS_TEXT);
         return true;

     }

Same thing I've answered i: EditText with number keypad by default, but allowing alphabetic characters

Community
  • 1
  • 1
Saurav
  • 560
  • 4
  • 17
  • The solution of Vikram also changes `inputType` as you do. I could additionally use the pound key also. But the real problem lies somewhere else: Check [my chat with Vikram](http://chat.stackoverflow.com/rooms/73469/discussion-between-vikram-and-oneworld) . Biggest problem is a samsung device handling the input type class "number". When this `inputType` is set, all letters get hidden from the `EditText`. Thanks for pointing to the other thread. – OneWorld Mar 24 '15 at 07:27
  • @OneWorld I had closed the chat window after few hours of keeping it around. Send me a message there when you get a chance, – Vikram Mar 24 '15 at 13:14
  • It's still linked in my first comment on your answer – OneWorld Mar 24 '15 at 13:15
  • @OneWorld I know. I assumed that you would leave me a comment here if you wanted to discuss further. – Vikram Mar 24 '15 at 13:25
  • This approach will most probably not work. Soft keyboards do not (or at least, are discouraged to) send key events. I am not sure if `KEYCODE_POUND` is an exception to this (I know that some keys are). But, what has `KEYCODE_POUND` got to do with this? On a side note, key events are sent by hardware keyboards. – Vikram Mar 24 '15 at 14:45