在日常開發中,Activity中可能有比較複雜的佈局,比如嵌套很多Fragment,所以針對該需求,通常的做法爲放到Activity中統一處理。
常規思路爲在dispatchTouchEvent中不攔截MotionEvent,但是會根據event是否落在EditText中做相應的處理,具體做法爲
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if (isShouldHideInput(v, ev)) {
hideInputMethod(this, v);
}
}
return super.dispatchTouchEvent(ev);
}
private boolean isShouldHideInput(View v, MotionEvent event) {
if (v != null && (v instanceof EditText)) {
int[] leftTop = {0, 0};
v.getLocationInWindow(leftTop);
int left = leftTop[0], top = leftTop[1], bottom = top + v.getHeight(), right = left
+
v.getWidth();
if (event.getX() > left && event.getX() < right
&& event.getY() > top && event.getY() < bottom) {
return false;
} else {
return true;
}
}
return false;
}
private Boolean hideInputMethod(Context context, View v) {
v.clearFocus();
InputMethodManager imm = (InputMethodManager)context
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
return false;
}
在Down事件時如果event不在當前獲取焦點的EditText中,則收起鍵盤,另外清除焦點,注意EditText的parent需要設置如下屬性保證可以獲取焦點,一般直接設置在activity的頂層佈局中
android:focusable="trur"
android:focusableInTouchMode="trur"
如上處理後會發現用戶在EditText間切換焦點時不正常,鍵盤會出現先收起再彈出,我們要做的是能判斷MotionEvent消費後是否由其它EditText獲取到了焦點,如果是的話則不收起鍵盤。
當用戶點擊其它EditText時,只有Up事件發生時纔會完成focus切換,所以做法有兩種,第一種比較簡單,延時200ms再判斷就可以覆蓋絕大部分場景,不過會出現用戶按住其它EditText時鍵盤收起等情況。
private boolean hideInputMethod(Context context, View v) {
// 延時100ms判斷焦點是否還在edittext,不在的話則隱藏鍵盤
mHandler.postDelayed(() -> {
View current = getCurrentFocus();
if (current == v) {
v.clearFocus();
InputMethodManager imm = (InputMethodManager)context
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
}, 200);
return false;
}
另外一個更好的方法是在dispatchTouchEvent中監聽焦點的變化,如果down和up時焦點無變化,則認爲需要收起鍵盤,與上面類似。具體代碼如下
boolean pendingCollapseKeyword = false;
View focusedView;
// 點擊非輸入框位置優先隱藏軟鍵盤
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
pendingCollapseKeyword = isShouldHideInput(ev);
if (pendingCollapseKeyword) focusedView = getCurrentFocus();
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
if (pendingCollapseKeyword) {
hideInputMethod(this);
}
}
return super.dispatchTouchEvent(ev);
}
private boolean isShouldHideInput(MotionEvent event) {
View v = getCurrentFocus();
if (v instanceof EditText) {
int[] location = {0, 0};
v.getLocationInWindow(location);
return event.getX() < location[0]
|| event.getX() > location[0] + v.getWidth()
|| event.getY() < location[1]
|| event.getY() > location[1] + v.getHeight();
}
return false;
}
// 擡起手指時如果焦點還在原來的EditText則收起鍵盤
private void hideInputMethod(Context context) {
View v = getCurrentFocus();
if (v == focusedView) {
focusedView.clearFocus();
InputMethodManager imm = (InputMethodManager)context
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(focusedView.getWindowToken(), 0);
}
}
}
如上同時兼顧了兩個需求
- 點擊EditText外收起鍵盤
- 點擊其它EditText保持鍵盤狀態不變