我发布这个是因为我认为任何答案都没有真正解决这个问题。问题中的屏幕截图与特定 InputType 的默认状态不对应。因此,切换 InputTypes 不会为您提供屏幕截图中的布局。
(根据我的研究...)
对符号输入的支持不受任何合约的约束。创建自己的InputMethod 时,可以很好地忽略符号。或者,他们可以添加分页支持以提供对 100 个符号的访问。这可以受合同约束吗?也许。但是,目前还没有。
输入法框架不允许客户端和 IME 之间直接通信。所有通信都通过InputMethodManager 或InputConnection 进行——一种单向通道。然而,使用?123 切换到符号是一个内部事件——不是定义的状态/动作。客户端应用程序无法切换到它。没有公共(或隐藏)API 可以实现这一点。
InputType 表示与 IME 完全不同的东西。不知道为什么每个人都推荐使用它。 您当然会发现特定的InputType 提供了大部分必需的密钥。但这和show[ing] Android keyboard with symbols mode by default.不一样
可能的解决方法:
我们将创建一个自定义EditText。我们不必。它将所有内容保存在一个地方,让我们免于复制粘贴的噩梦。
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;
}
});
}
}
接下来,我们需要定义definedActionId。打开或创建res/values/integers.xml 并添加:
<integer name="definedActionId">-100</integer>
-100 是一个任意值。我检查了 EditorInfo 并且 actionIds (IME_ACTION_XXXX) >= 0。-100 似乎是一个不错的候选者。
在 xml 中,您的布局将如下所示:
<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 -->
没什么好解释的。 IME 将以 NUMBER 模式启动。它将显示ABC,而不是复选标记图标。单击时,我们拦截 actionId 并在 NUMBER 和 TEXT 输入之间切换。我们使用setInputType(...),因为它不仅更新了InputType,还通过更改重新启动了IME。 setRawInputType(...) 只更新InputType。
问题:
如您所知,这并不是真正的解决方案。如果用户在 TEXT 模式下关闭键盘(使用back 按钮),当他们再次打开键盘时,键盘将保持在 TEXT 模式。要进入数字模式,用户必须单击NUM。此外,在 TEXT 模式下,用户将看到 NUM 作为操作,以及 ?123 选项。这不会破坏任何东西,但确实会影响用户体验。
由于上述原因,我们无法对在 TEXT 模式下显示的 ?123 做任何事情。但是,我们可以尝试确保键盘始终以数字模式打开。我将提供一个粗略的草图,说明我们将如何做到这一点。它不是直截了当的,因为我们(开发人员)不知道诸如键盘关闭或打开之类的事件。更新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();
}
}
}
同样,代码是不言自明的。我们创建一个InputConnectionWrapper 并监听finishComposingText() 回调。如果我们在TEXT 和NUMBER 之间手动切换,我们会使用一个标志,因为finishComposingText() 会自动被调用。否则,我们检查输入类型是否设置为TEXT,并将其更改为NUMBER。我不确定finishComposingText() 是否是解释键盘关闭/打开的正确方法。在 API 21、vanilla android 上进行测试,这似乎可行。需要进行更多测试。
我真的希望有人能提出比这更好、更强大的解决方案 - 或者修改我的解决方法,使它看起来不像一个。
总结
当前的任务是提供围绕现有输入法引擎 (IME) 在 NUMBER 和 TEXT 输入模式之间切换的功能。第一种方法是在切换机制中使用imeActionLabel & imeActionId。这种方法适用于 Google 的键盘 (this is the imeActionLabel),但不适用于三星的键盘 - imeActionLabel failed to show up in portrait(没有 extract)。可能的解决方法是在应用自己的 UI 中包含切换按钮。
即使使用 Google 的键盘,在输入字母后模式切换回 NUMBER 时,字母(文本)也无法显示。此问题已通过使用标志 flagNoExtractUi 修复(至少在测试设备上),该标志可防止 IME 在横向进入全屏模式。
最终解决方案(待实施和测试)
- IME 以数字输入模式启动(95% 的用例涉及数字输入)
- 在应用的 UI 中(
EditText 旁边)添加了一个按钮,用于在 NUMBER 和 TEXT 模式之间切换
- 用户可以不受任何限制地从 NUMBER 切换到 TEXT。从 TEXT 切换回 NUMBER 要求未添加任何字母。
- InputType 在键盘关闭和重新打开之间保留。示例:如果用户切换到 TEXT 模式并关闭键盘,它将以 TEXT 模式打开。 InputType 未重置。
有关尝试的方法的更多信息,请参阅this discussion thread。
截图
默认(数字):
切换到 TEXT:
Recorded video link