在汽车车载娱乐系统应用开发中,有相当一部分车机采用宽矮屏幕,即:屏幕的宽度大但高度小。针对这种情况,难免遇到输入法弹出后覆盖内容过多的情况,特别是页面存在多个输入框时,几个输入框既要满足在输入法未弹出时的全屏布局,又要满足输入法弹出时合理适时地出现在用户视野范围内,否则非常影响用户体验。
参考如下需求:
1. 输入法未弹出时,页面中存在多个输入框,并纵向排布,铺满整个屏幕
2. 点击某个输入框时,输入法键盘弹出,键盘会占据大半屏幕,此时需要将点击的输入框聚焦到可见范围
3. 输入法弹出时,拖动非键盘范围可以自由将其他输入框拖至可见范围
4. 当输入框失去焦点时,需要检测当前输入框内容是否符合要求,若不符合,则重新抢回焦点并高亮提示
综上,整个页面涉及到的技术点有:输入法键盘弹出收起的监听,页面焦点切换的监听,字符输入的监听等,废话不多说,看代码:
首先是常规的输入框控件绑定和监听:
@BindView(R.id.pvPassword)
Edittext mPasswordView;
TextListener textListener = new TextListener();
mPasswordView.addTextChangedListener(textListener);
FocusListener focusListener = new FocusListener();
mPasswordView.setOnFocusChangeListener(focusListener);
KeyboardListener hideListener = new KeyboardListener();
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(() -> {
//此处代码为判断键盘弹出
InputMethodManager inputManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
int inputWindowVisibleHeight = inputManager.getInputMethodWindowVisibleHeight();
//监听键盘变化的回调
if (inputWindowVisibleHeight == 0) {
hideListener .onHideStatusChanged(true);
} else {
hideListener .onHideStatusChanged(false);
}
});
之后是各个Listener的定义:
public class FocusListener implements View.OnFocusChangeListener {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
updateErrorView(v); // 更新当前报错显示
} else {
// requireCurrentErrorView()为获取当前报错输入框View
if (requireCurrentErrorView() != null && requireCurrentErrorView() != v) {
requireCurrentErrorView().requestFocus();
} else if (requireCurrentErrorView() == null || requireCurrentErrorView() == v) {
getWindow().getDecorView()
.postDelayed(SignUpFullScreenDialog.this::resizeEdittext, 300);
}
}
}
}
public class TextListener implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
// 重置所有edittext View
updateButtonStatus();
}
}
public class KeyboardListener implements KeyboardStatusChangedListener {
@Override
public void onKeyboardStatusChanged(boolean isHide) {
transformWhileKeyboardHide(isHide);
if (isHide) {
if (mLayout != null) {
mLayout .smoothScrollTo(0, 0);
}
} else {
getWindow().getDecorView()
.postDelayed(SignUpFullScreenDialog.this::resizeEdittext, 300);
}
}
}
最后是关键方法:
private void resizeEdittext() {
if (mLayout != null && mET1 != null && mET1.hasFocus()) {
mLayout.smoothScrollTo(0, 0);
} else if (mLayout != null && mET2 != null && mET2.hasFocus()) {
mLayout.smoothScrollTo(0, 136);
} else if (mLayout != null && mET3 != null && mET3.hasFocus()) {
mLayout.smoothScrollTo(0, 300);
} else if (mLayout != null && mET4 != null && mET4.hasFocus()) {
mLayout.smoothScrollTo(0, 438);
}
}
private void transformWhileKeyboardHide(boolean isHide) {
if (mLayout != null) {
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mSignUpLayout.getLayoutParams();
params.height = isHide ? ViewGroup.LayoutParams.WRAP_CONTENT : 248;
mLayout.setLayoutParams(params);
}
}