EditView外部点击隐藏软键盘需求
业务场景
在工作过程中,遇到一个需求。某个页面,需要在EditView输入数值,在软键盘下滑和点击EditView外部区域后,EditView失去焦点,并且开始计算最终金额。因为该页面没有其他控件可以抢占EditView焦点,所以需要做一些额外处理来完成这个需求。下面是步骤
第一步,重写EditView父控件的事件分发方法,判断是否有外部点击
使用下面这个类,判断是否是EditView的外部点击。还可以调用addList(View)方法,把该父布局下的子控件同时加入判断,判断这些子控件和EditView的区域外点击。
调用步骤,
- 在xml中,把这个DispatchTouchFrameLayout当作EditView的父控件,这里也可以修改成RelativeLayout或者LinearLayout都可以。
- setEditView(EditView) 加入判断的editView
- addList(View) 加入需要判断的其他View,可选
- setRunnable(Runnable),添加触发后的响应事件
/**
* created time: 2019/6/18
* 可判断是否在EditView以外区域发生了点击
*/
public class DispatchTouchFrameLayout extends FrameLayout {
public DispatchTouchFrameLayout(@NonNull Context context) {
super(context);
}
public DispatchTouchFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public DispatchTouchFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
EditText editText;
//其他不响应区域view
ArrayList<View> notCheckViewList = new ArrayList<>();
public void addList(View view) {
notCheckViewList.add(view);
}
private EditText getEditText() {
return editText;
}
public void setEditText(EditText editText) {
this.editText = editText;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
this.editText = null;
notCheckViewList.clear();
notCheckViewList = null;
}
//点击以外区域后的响应事件
Runnable runnable;
public void setRunnable(Runnable runnable) {
this.runnable = runnable;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
EditText v = getEditText();
if (isShouldHideInput(v, ev)) {
if (runnable != null)
runnable.run();
}
}
return super.dispatchTouchEvent(ev);
}
/**
* 是否应该触发eidtView区域外的点击事件
*
* @param v
* @param event
* @return
*/
private boolean isShouldHideInput(EditText v, MotionEvent event) {
if (v != null && v.isFocused()) {
boolean isOnEditView = isOnViewArea(v, event);
if (isOnEditView) return false;
//点击其他list内view区域,也不响应
for (View view : notCheckViewList) {
boolean onViewArea = isOnViewArea(view, event);
isOnEditView = isOnEditView || onViewArea;
}
return !isOnEditView;
}
return false;
}
/**
* 判断某个点击时间是否在这个view区域内
*
* @param view
* @param event
* @return true表示在范围内
*/
private boolean isOnViewArea(View view, MotionEvent event) {
if (view == null) return false;
int[] leftTop = {0, 0};
//获取输入框当前的location位置
view.getLocationInWindow(leftTop);
//取window坐标
int[] containerTop = {0, 0};
getLocationInWindow(containerTop);
int left = leftTop[0];
int top = leftTop[1];
int bottom = top + view.getHeight();
int right = left + view.getWidth();
float x = containerTop[0] + event.getX();
float y = containerTop[1] + event.getY();
return x > left && x < right
&& y > top && y < bottom;
}
}
第二步,监听软键盘下滑事件
监听软件盘下滑事件,setOnSoftKeyBoardChangeListener(), keyBoardHide()触发时响应runnable
/**
* description: 软键盘弹起和下滑监听类
*/
public class KeyBoardChangeListener implements ViewTreeObserver.OnGlobalLayoutListener {
View rootView;
int rootViewVisibleHeight; //纪录根视图的显示高度,每次改变重新赋值
final int HEIGHT_BOUND = 200; //200px为判断界限值
public KeyBoardChangeListener(Activity activity) {
if (activity == null)
return;
rootView = findContentView(activity);
addContentTreeObserver();
}
private View findContentView(Activity activity) {
return activity.getWindow().getDecorView();
}
private void addContentTreeObserver() {
if (rootView != null)
rootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
public void onGlobalLayout() {
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
int visibleHeight = r.height();
if (rootViewVisibleHeight == 0) { //初始化高度
rootViewVisibleHeight = visibleHeight;
return;
}
//可见高度不变,未发生软键盘变化
if (rootViewVisibleHeight == visibleHeight)
return;
int diffHeight = Math.abs(rootViewVisibleHeight - visibleHeight);
if (diffHeight > HEIGHT_BOUND) { //如果变化高度大于界限
if (rootViewVisibleHeight > visibleHeight) { //可见高度变小,表示软键盘弹出
if (onSoftKeyBoardChangeListener != null)
onSoftKeyBoardChangeListener.keyBoardShow(diffHeight);
} else {
if (onSoftKeyBoardChangeListener != null)
onSoftKeyBoardChangeListener.keyBoardHide(visibleHeight - rootViewVisibleHeight);
}
rootViewVisibleHeight = visibleHeight;
}
}
private OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener;
public void setOnSoftKeyBoardChangeListener(OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) {
this.onSoftKeyBoardChangeListener = onSoftKeyBoardChangeListener;
}
public interface OnSoftKeyBoardChangeListener {
void keyBoardShow(int height);
void keyBoardHide(int height);
}
public void release() {
if (rootView != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
rootView = null;
}
}
第三步,隐藏软键盘和取消EditView焦点
运行的runnable事件中,加上隐藏软键盘,取消EditView焦点,开始计算等逻辑即可
//隐藏软键盘
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(((Activity) context).getWindow().getDecorView().getWindowToken(), 0);
//EditView取消焦点和光标
editView.clearFocus();