Android 軟鍵盤響應事件解析

大頭呆發表於2017-12-21

前言

最近重新設計了一遍專案中的搜尋欄,但是目前這個輸入框每次填完搜尋內容,都需要去按下右邊的搜尋按鈕,感覺比較麻煩。但是看到很多應用,填完內容後輸入框右下角按鈕直接會變成搜尋按鈕。平時對這方面瞭解比較少,故在此總結下Android軟鍵盤響應事件。

imeOptions

要想實現這個小功能,就不得不說這個屬性,對應程式碼中的方法是:

void setImeOptions(int imeOptions)int getImeOptions()

imeOptions常用的有以下幾種:

  • actionDone 完成
  • actionGo 前進
  • actionNext 下一項
  • actionNone 無動作
  • actionPrevious 上一項
  • actionSearch 搜尋
  • actionUnspecified 未指定
  • actionSend 傳送

意思都很好理解 ,其中預設動作是actionUnspecified,在程式碼中這些值儲存在EditorInfo類中,以IME開頭,如EditorInfo.IME_ACTION_GO。所以上面我們需要實現的搜尋功能只是這些動作中的一種。另外還需要設定一個屬性,不然無法生效:

android:singleLine="true"

我的理解是,如果不設定限制單行,那麼右下角會被換行按鈕佔用,這個功能是高於鍵盤動作事件優先順序的。 另外可能有些輸入法還要求設定: android:inputType="text"

不過我實測不設定也可以(當然為了相容性還是建議設一下)。好了,這樣設完之後我們就可以在程式碼中監聽這個動作的事件了:

 mEditText.setOnEditorActionListener(new EditText.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                    String text = mEditText.getText().toString();
                    if (TextUtils.isEmpty(text)) {
                        Toast.makeText(MainActivity.this, "請輸入關鍵字", Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(MainActivity.this, "輸入了" + text, Toast.LENGTH_SHORT).show();
                    }
                    return true;
                }
                return false;
            }
        });
複製程式碼

看下效果:

Android 軟鍵盤響應事件解析
返回true說明消耗了這個事件,不然會繼續執行這個動作預設的操作,這點在原始碼上很容易體現:

 public void onEditorAction(int actionCode) {
        final Editor.InputContentType ict = mEditor == null ? null : mEditor.mInputContentType;
        if (ict != null) {
            if (ict.onEditorActionListener != null) {
                if (ict.onEditorActionListener.onEditorAction(this,
                        actionCode, null)) {
                    return;
                }
            }
            
    ......執行預設動作
複製程式碼

在原始碼中看出,系統實現的預設動作只有三個:

 if (actionCode == EditorInfo.IME_ACTION_NEXT) {
                View v = focusSearch(FOCUS_FORWARD);
                if (v != null) {
                    if (!v.requestFocus(FOCUS_FORWARD)) {
                        throw new IllegalStateException("focus search returned a view "
                                + "that wasn't able to take focus!");
                    }
                }
                return;

            } else if (actionCode == EditorInfo.IME_ACTION_PREVIOUS) {
                View v = focusSearch(FOCUS_BACKWARD);
                if (v != null) {
                    if (!v.requestFocus(FOCUS_BACKWARD)) {
                        throw new IllegalStateException("focus search returned a view "
                                + "that wasn't able to take focus!");
                    }
                }
                return;

            } else if (actionCode == EditorInfo.IME_ACTION_DONE) {
                InputMethodManager imm = InputMethodManager.peekInstance();
                if (imm != null && imm.isActive(this)) {
                    imm.hideSoftInputFromWindow(getWindowToken(), 0);
                }
                return;
            }
        }
複製程式碼

有一點需要提一下:上面程式碼這麼設定,點完搜尋按鈕,事件響應了但輸入框是不會消失的,所以這裡我們可以取巧下:處理完我們的動作後直接把動作設定為IME_ACTION_DONE,然後返回false,這樣就可以繼續執行EditorInfo.IME_ACTION_DONE中的程式碼,從而實現在點完搜尋按鈕後隱藏輸入框:

 mEditText.setOnEditorActionListener(new EditText.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                    String text = mEditText.getText().toString();
                    if (TextUtils.isEmpty(text)) {
                        //如果沒輸入內容就不隱藏了
                        Toast.makeText(MainActivity.this, "請輸入關鍵字", Toast.LENGTH_SHORT).show();
                        return true;
                    } else {
                        Toast.makeText(MainActivity.this, "輸入了" + text, Toast.LENGTH_SHORT).show();
                        mEditText.setImeOptions(EditorInfo.IME_ACTION_DONE);
                        return false;
                    }

                }
                return false;
            }
        });
複製程式碼

小tips

  1. 上面提到必須要設定android:singleLine="true",但是我們想換行怎麼辦? 在程式碼中這樣設定就行了:
mEditText.setHorizontallyScrolling(false);
mEditText.setMaxLines(4);
複製程式碼

在佈局檔案中設定是無效的,應該也是優先順序的問題。加第一行程式碼是因為EditTextview內容超過一行是不會換行的而是左右滾動。

  1. 想自定義鍵盤上事件響應的按鈕是不行的,只能從已經設定好的幾個動作中選擇,由於第三方輸入法的不同,有些動作不會響應,顯示圖示也不盡相同。

  2. 我們是根據actionId而不是直接採用mEditText.getImeOptions(),兩個值初始時是一樣的,但第一次軟體盤事件響應後,後者值就變了,而actionId還是原來的。軟鍵盤右下角按鈕的動作是取決於actionId的,這樣我們也就不需要在鍵盤消失後將mEditText的動作還原。

相關文章