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.
Is there a way to achieve that? If yes, how?
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)
EditText
) for switching between NUMBER & TEXT modeFor more information about the approaches tried, refer to this discussion thread.
Screenshots
Default (NUMBER):
Switched to TEXT: