android點擊EditText外區域收起鍵盤

在日常開發中,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);
            }
        }
    }

如上同時兼顧了兩個需求

  1. 點擊EditText外收起鍵盤
  2. 點擊其它EditText保持鍵盤狀態不變
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章