- IM(Input Method): 输入法。是指通过键盘等输入设备输入 输入设备上没有 的字符的 方法/程序/处理器 。最开始是特指在拉丁字母键盘上输入CJK (Chinese, Japanese and Korean)文字的方法,现在一般将所有的输入语言字符的方式都统称为输入法。
- IME(Input Method Editor): 输入法编辑器。通常简作输入法。
- IMF(input method framework): 输入法框架。
输入法样式的控制
Android系统是定制的Linux系统,支持物理输入设备输入和软输入的输入方式,通常在没有物理键盘的设备上当输入框获取焦点时系统会自动唤起软键盘以便用户输入(on-screen keyboard),现在的Android设备上基本都没有物理键盘,都是通过软键盘的方式输入数据的,所以这里只考虑软输入方式。
android:inputType
我们可以根据文本框要输入数据的类型(邮箱、手机号、密码、时间…)告诉系统我们更想显示哪种类型的输入法键盘(type可以多个配合使用,使用”|”连接起来)。如android:inputType="phone"
系统就优先显示数字键盘以便输入手机号。
- none.
- text.
- textCapCharacters. 和text配合使用以大写所有字符
- textCapWords. 和text配合使用以大写每个单词的第一个字母
- textCapSentences. 和text配合使用以大写每个句子的第一个字母
- textAutoCorrect. 和text配合使用以自动更正
- textAutoComplete. 和text配合使用以自动提示补全输入(需要AutoCompleteTextView)
- textMultiLine. 和text配合使用以使输入框允许显示多行(默认输入框只能单行显示)
- textImeMultiLine. 和text配合使用以表明输入框不能是多行的,希望IME输入法能提供多行输入支持。
- textNoSuggestions. 和text配合使用以告知IME输入法不允许显示任何基于字典的单词建议提示。
- textUri. 输入将被用作URI
- textEmailAddress. 输入将被用作e-mail地址
- textEmailSubject. 输入将被用作e-mail主题
- textShortMessage. 输入将被用作短消息内容
- textLongMessage. 输入将被用作长消息内容
- textPersonName. 输入将被用作人名
- textPostalAddress. 输入将被用作邮寄地址
- textPassword. 输入将被用作密码
- textVisiblePassword. 输入将被用作可见密码
- textWebEditText. 输入将被用作Web表单文本
- textFilter. 输入将被用作其它数据的过滤器
- textPhonetic. 输入将被用作语音符号,例如联系人列表中的拼音/语音符号字段
- textWebEmailAddress. 输入将被用作Web表单e-mail地址
- number. 输入将仅允许数字输入
- numberSigned. 和number配合使用以输入有符号数
- numberDecimal. 和number配合使用以输入小数
- numberPassword. 输入将被用作数字密码
- phone. 输入将被用作手机号
- datetime. 输入将被用作日期和时间
- date. 输入将被用作日期
- time. 输入将被用作时间
android:imeOptions
我们可以根据输入框输入完成后要执行的业务逻辑指定软键盘右下角Action按钮的样式和行为,如让右下角按钮显示为发送按钮,点击后执行发送消息的逻辑。值得注意的是EditText输入框默认是可以多行显示的,此时的右下角Action按钮只会作为换行按钮,无论你的imeOptions
指定的是什么。
如果想监听并处理Action按钮的点击逻辑,可以为EditText
设置TextView.OnEditorActionListener
。
- IME_ACTION_UNSPECIFIED. 编辑器自己决定Action按钮的行为
- IME_ACTION_GO. Action按钮将作为go(开始)按钮。点击后跳转到输入字符的意图页面
- IME_ACTION_SEARCH. Action按钮将作为search(搜索)按钮。点击后跳转到输入字符的搜索结果页面
- IME_ACTION_SEND. Action按钮将作为send(发送)按钮。点击后将输入字符发送给它的目标
- IME_ACTION_NEXT. Action按钮将作为next(下一个)按钮。点击后将进行下一个输入框的输入
- IME_ACTION_DONE. Action按钮将作为done(完成)按钮。点击后IME输入法将会关闭
- IME_ACTION_PREVIOUS. Action按钮将作为previous(上一个)按钮。点击后将进行上一个输入框的输入
- IME_FLAG_NO_FULLSCREEN. 请求IME输入法永远不要进入全屏模式
- IME_FLAG_NAVIGATE_PREVIOUS. 类似
IME_FLAG_NAVIGATE_NEXT
, 表明这里有后退导航可以关注的兴趣点 - IME_FLAG_NAVIGATE_NEXT. 表明这里有前进导航可以关注的兴趣点,类似
IME_ACTION_NEXT
,不过允许IME输入多行且提供前进导航。 - IME_FLAG_NO_EXTRACT_UI. 请求IME输入法不要显示额外的文本UI
- IME_FLAG_NO_ACCESSORY_ACTION. 和一个Action结合使用表明在全屏输入法中不作为可访问性按钮
- IME_FLAG_NO_ENTER_ACTION. 如果未设置,IME输入法将把Enter按钮自动替换为Action按钮,多行文本将自动set该标志位
- IME_FLAG_FORCE_ASCII. 请求IME输入法接受ASCII字符的输入
输入法可见性的控制
当EditText输入框获取或失去焦点时,Android系统会视情况显示或隐藏输入法。同时系统也会决定你的UI和输入框如何显示在输入法上方,例如:当竖直方向空间被约束时,输入框也许会填充输入法上方的所有空间,大多数App都是使用这种默认输入行为。
显示输入法
默认情况下,进入Activity时即使EditText获取到焦点也不会自动弹出输入法的软键盘,因为进入该Activity的第一件事可能并不是输入数据。如果你想在进入该Activity时就显示输入法,只需为<activity>
元素指定android:windowSoftInputMode="stateVisible"
属性即可(如果设备有物理键盘则不会显示输入法)。
如果要手动显示输入法,则只需先让View获取焦点(调用requestFocus()
),然后使用InputMethodManager
的showSoftInput()
方法显示输入法:
public void showSoftKeyboard(View view) {
if (view.requestFocus()) {
InputMethodManager imm = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
}
注意:一旦输入法显示出来,你就不能通过代码隐藏它,系统会在用户完成输入或者按返回按钮时隐藏输入法。
输入法对UI布局的影响
当输入法显示在屏幕上,势必会占用大量的屏幕空间。通常情况下系统会决定调整你的UI在屏幕中的位置以便给输入法留出空间,以保证你的UI元素不被输入法遮挡。但有些情况我们为了更好地用户体验,并不希望系统将我们的UI简单粗暴地移动位置,此时就需要我们手动指定系统该如何在剩下的空间里显示我们的UI。
例如:为了确保在输入法显示时我们可以访问当前页面所有的布局元素,我们可以使用android:windowSoftInputMode="adjustResize"
,系统会根据情况自动调整其它View尺寸。而如果我们不希望布局因软键盘而变形,我们可以使用android:windowSoftInputMode="adjustPan"
,系统会根据情况自动调整View的位置以使输入框可见。
系统最终如何调整你的UI元素的大小和位置取决于你的你的布局和元素属性;所以动态合理地使用adjustPan
和adjustResize
以保证你的UI元素能更好的响应输入法的显示与隐藏。
最一般的情况,输入框不是常驻页面,而是作为浮动框输入数据,此时我们不希望下层页面因为输入框的出现而变形或移动,那么可以采用多层Fragment的形式输入数据,如:
public class InputDialogFragment extends DialogFragment {
private static final String ARG_EDIT_TEXT_HINT = "edit_text_hint";
private static final String ARG_EDIT_TEXT_CONTENT = "edit_text_content";
private String mTextHint;
private String mTextContent;
public InputDialogFragment() {
}
public static InputDialogFragment newInstance(String edit_text_hint, String edit_text_content) {
InputDialogFragment fragment = new InputDialogFragment();
Bundle args = new Bundle();
args.putString(ARG_EDIT_TEXT_HINT, edit_text_hint);
args.putString(ARG_EDIT_TEXT_CONTENT, edit_text_content);
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (getArguments() != null) {
mTextHint = getArguments().getString(ARG_EDIT_TEXT_HINT);
mTextContent = getArguments().getString(ARG_EDIT_TEXT_CONTENT);
}
Dialog dialog = getDialog();
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
dialog.setCanceledOnTouchOutside(true);
dialog.getWindow().getAttributes().windowAnimations = R.style.InputDialog;
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
return inflater.inflate(R.layout.fragment_input_dialog, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
final View vg_comment_box = view.findViewById(R.id.vg_comment_box);
final EditText et_edit_comment = (EditText) view.findViewById(R.id.et_edit_comment);
final TextView tv_send = (TextView) view.findViewById(R.id.tv_send);
et_edit_comment.setHint(mTextHint);
et_edit_comment.setText(mTextContent);
vg_comment_box.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showSoftKeyboard(et_edit_comment);
}
});
et_edit_comment.post(new Runnable() {
@Override
public void run() {
showSoftKeyboard(et_edit_comment);
}
});
tv_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getContext(), "Send:" + et_edit_comment.getText().toString(), Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onStart() {
super.onStart();
getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
final Resources res = getResources();
final int titleDividerId = res.getIdentifier("titleDivider", "id", "android");
if (titleDividerId > 0) {
final View titleDivider = getDialog().findViewById(titleDividerId);
if (titleDivider != null) {
titleDivider.setBackgroundColor(res.getColor(android.R.color.transparent));
}
}
}
public void showSoftKeyboard(View view) {
if (view.requestFocus()) {
InputMethodManager imm = (InputMethodManager)
getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
}
}
<style name="InputDialog" parent="@android:style/Theme.Dialog">
<item name="android:background">@android:color/transparent</item>
<item name="android:windowEnterAnimation">@null</item>
<item name="android:windowExitAnimation">@null</item>
</style>
<activity
android:name=".PostActivity"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="portrait" />
输入数据时时只需要弹出这个DialogFragment
即可:
InputDialogFragment inputDialogFragment = InputDialogFragment.newInstance(mHint, mContent);
inputDialogFragment.show(getFragmentManager(), "InputDialogFragment");
当然,如果想让输入框Fragment和Activity进行交互,只需要在InputDialogFragment
中定好接口,让Activity去实现就可以了。