Phát hiện khi người dùng bỏ qua bàn phím mềm


114

Tôi có một tiện ích EditText trong chế độ xem của mình. Khi người dùng chọn tiện ích EditText, tôi hiển thị một số hướng dẫn và bàn phím mềm xuất hiện.

Tôi sử dụng OnEditorActionListener để phát hiện khi nào người dùng đã hoàn thành nhập văn bản và tôi loại bỏ bàn phím, ẩn hướng dẫn và thực hiện một số hành động.

Vấn đề của tôi là khi người dùng loại bỏ bàn phím bằng cách nhấn phím QUAY LẠI. Hệ điều hành loại bỏ bàn phím, nhưng hướng dẫn của tôi (mà tôi cần ẩn) vẫn hiển thị.

Tôi đã thử ghi đè OnKeyDown, nhưng điều đó dường như không được gọi khi nút BACK được sử dụng để loại bỏ bàn phím.

Tôi đã thử đặt OnKeyListener trên tiện ích EditText, nhưng dường như điều đó cũng không được gọi.

Làm cách nào tôi có thể phát hiện khi bàn phím mềm đang bị loại bỏ?

Câu trả lời:


160

Tôi biết một cách để làm điều này. Phân lớp EditText và triển khai:

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
  if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
    // Do your thing.
    return true;  // So it is not propagated.
  }
  return super.dispatchKeyEvent(event);
}

Đây là liên kết về cách sử dụng chế độ xem tùy chỉnh của bạn (khi bạn phân lớp EditText): http://developer.android.com/guide/topics/ui/custom-components.html


2
Tôi nhận được báo cáo từ người dùng Android có bàn phím Phần cứng rằng việc này bằng cách nào đó gây cản trở việc nhấn phím. Tôi không có bất kỳ thông tin bổ sung nào vào lúc này.
esilver

Tôi đã tìm kiếm một số giải pháp, đây là giải pháp tốt nhất!
Friesgaard

10
Chờ đã, đợi đã, tôi vừa mới nhìn lần thứ ba - không phải là cuộc gọi siêu onKeyPreImesao? Hay có một lý do cụ thể nào khiến nó không được như vậy?
Erhannis

Có vẻ hữu ích, ngoại trừ trường hợp EditText không thể được phân lớp (ví dụ: trong SearchView). Đây là sự cố khi cố gắng ẩn Chế độ xem tìm kiếm nếu trống khi bàn phím bị loại bỏ. Tôi phải tự hỏi tại sao những người Android không chỉ cung cấp một số API OSK đẹp cho loại thứ này.
tbm

2
@tbm Để đạt được hiệu quả tương tự trong SearchView, vui lòng tham khảo stackoverflow.com/questions/9629313/…
Cheok Yan Cheng

124

Jay, giải pháp của bạn là tốt! cảm ơn :)

public class EditTextBackEvent extends EditText {

    private EditTextImeBackListener mOnImeBack;

    public EditTextBackEvent(Context context) {
        super(context);
    }

    public EditTextBackEvent(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public EditTextBackEvent(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && 
            event.getAction() == KeyEvent.ACTION_UP) {
            if (mOnImeBack != null) 
                mOnImeBack.onImeBack(this, this.getText().toString());
        }
        return super.dispatchKeyEvent(event);
    }

    public void setOnEditTextImeBackListener(EditTextImeBackListener listener) {
        mOnImeBack = listener;
    }

}

public interface EditTextImeBackListener {
    public abstract void onImeBack(EditTextBackEvent ctrl, String text);
}

Bất kỳ lý do cụ thể nào mà chúng tôi cũng muốn phát hiện KeyEvent.ACTION_UP?
Cheok Yan Cheng

2
@CheokYanCheng đó là do hành động của người dùng thường có hiệu lực khi nhả nút, chứ không phải khi bắt đầu nhấn.
jayeffkay

3
Đảm bảo kéo dài thời gian android.support.v7.widget.AppCompatEditTextpha màu.
Sanvywell

Mở rộng: AppCompatEditTextcho androidx
COYG

Tuyệt quá! Tôi chỉ đề xuất một cải tiến chỉ để tổng quát hóa giải pháp của bạn. Tôi sẽ chuyển các đối số từ onKeyPreIme "nguyên trạng" cho người nghe, bằng cách này, bạn có thể triển khai logic của mình theo những cách khác nhau khi bạn cần.
marcRDZ

17

Tôi đã thực hiện một thay đổi nhỏ về giải pháp của Jay bằng cách gọi super.onKeyPreIme ():

_e = new EditText(inflater.getContext()) {
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK){
            cancelTextInput();
        }
        return super.onKeyPreIme(keyCode, event);
    }
};

Giải pháp tuyệt vời, Jay, +1!


14

Đây là EditText tùy chỉnh của tôi để phát hiện xem bàn phím có đang hiển thị hay không

/**
 * Created by TheFinestArtist on 9/24/15.
 */
public class KeyboardEditText extends EditText {

    public KeyboardEditText(Context context) {
        super(context);
    }

    public KeyboardEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        if (listener != null)
            listener.onStateChanged(this, true);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
                && event.getAction() == KeyEvent.ACTION_UP) {
            if (listener != null)
                listener.onStateChanged(this, false);
        }
        return super.onKeyPreIme(keyCode, event);
    }

    /**
     * Keyboard Listener
     */
    KeyboardListener listener;

    public void setOnKeyboardListener(KeyboardListener listener) {
        this.listener = listener;
    }

    public interface KeyboardListener {
        void onStateChanged(KeyboardEditText keyboardEditText, boolean showing);
    }
}

8

Bây giờ là năm 2019 ...
Vì vậy, tôi đã tạo ra một giải pháp gọn gàng hơn với Kotlin

1.Tạo một chức năng mở rộng:

fun Activity.addKeyboardToggleListener(onKeyboardToggleAction: (shown: Boolean) -> Unit): KeyboardToggleListener? {
    val root = findViewById<View>(android.R.id.content)
    val listener = KeyboardToggleListener(root, onKeyboardToggleAction)
    return root?.viewTreeObserver?.run {
        addOnGlobalLayoutListener(listener)
        listener
    }
}

2.Trình nghe chuyển đổi ở đâu:

open class KeyboardToggleListener(
        private val root: View?,
        private val onKeyboardToggleAction: (shown: Boolean) -> Unit
) : ViewTreeObserver.OnGlobalLayoutListener {
    private var shown = false
    override fun onGlobalLayout() {
        root?.run {
            val heightDiff = rootView.height - height
            val keyboardShown = heightDiff > dpToPx(200f)
            if (shown != keyboardShown) {
                onKeyboardToggleAction.invoke(keyboardShown)
                shown = keyboardShown
            }
        }
    }
}

fun View.dpToPx(dp: Float) = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics).roundToInt()

3. Sử dụng nó trong bất kỳ Hoạt động nào đơn giản như sau :

addKeyboardToggleListener {shown ->
          // hurray! Now you know when the keyboard is shown and hidden!!
      }

Cảm ơn giải pháp Kotlin. Mặc dù khi tôi triển khai nó, tôi nhận thấy rằng nó kích hoạt người nghe nhiều lần cho một lần thay đổi bàn phím và cả khi khởi động. Tôi phải lưu trữ trạng thái mở / không mở và chỉ gọi người nghe khi giá trị thực sự khác nhau.
RandomEngy

@RandomEngy đã sửa lỗi này trong KeyboardToggleListener. Cảm ơn bạn đã chú ý
Leo Droidcoder

4

Chỉ cần tạo một lớp mở rộng Edittext và sử dụng edittext đó trong mã của bạn, bạn chỉ nên ghi đè phương thức sau trong edittext tùy chỉnh:

@Override
 public boolean onKeyPreIme(int keyCode, KeyEvent event) {
 if (keyCode == KeyEvent.KEYCODE_BACK) {

    //Here it catch all back keys
    //Now you can do what you want.

} else if (keyCode == KeyEvent.KEYCODE_MENU) {
    // Eat the event
    return true;
}
return false;}

Có cách nào để phát hiện khi bàn phím mở không?
bột366

3

Đây là một giải pháp với người nghe chính. Tôi không biết tại sao điều này hoạt động nhưng OnKeyListener hoạt động nếu bạn chỉ hoàn toàn ghi đè onKeyPreIme trên EditText tùy chỉnh của mình.

SomeClass.java

customEditText.setOnKeyListener((v, keyCode, event) -> {
            if(event.getAction() == KeyEvent.ACTION_DOWN) {
                switch (keyCode) {
                    case KeyEvent.KEYCODE_BACK:
                        getPresenter().onBackPressed();
                        break;
                }
            }
            return false;
        }); 

CustomEditText.java

@Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        return super.dispatchKeyEvent(event);
    }

3

Sử dụng câu trả lời của @ olivier_sdg, nhưng được chuyển đổi thành Kotlin:

class KeyboardEditText : AppCompatEditText {

    var listener: Listener? = null
  
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
  
    override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
        if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
            listener?.onImeBack(this)
        }
        return super.dispatchKeyEvent(event)
    }
  
    interface Listener {
        fun onImeBack(editText: KeyboardEditText)
    }

}

Sử dụng:

keyboardEditText.listener = object : KeyboardEditText.Listener {
    override fun onImeBack(editText: KeyboardEditText) {
        //Back detected
    }
}

2

Đối với bất kỳ ai muốn làm điều tương tự trong Xamarin, tôi đã dịch một số câu trả lời hàng đầu vì nó hơi khác một chút. Tôi đã tạo ý chính ở đây nhưng tóm lại, bạn tạo một EditText tùy chỉnh và ghi đè OnKeyPreImenhư vậy:

public class CustomEditText : EditText
{
    public event EventHandler BackPressed;

    // ...

    public override bool OnKeyPreIme([GeneratedEnum] Keycode keyCode, KeyEvent e)
    {
        if (e.KeyCode == Keycode.Back && e.Action == KeyEventActions.Up)
        {
            BackPressed?.Invoke(this, new EventArgs());
        }

        return base.OnKeyPreIme(keyCode, e);
    }
}

... và sau đó trong chế độ xem ...

editText = FindViewById<CustomEditText>(Resource.Id.MyEditText);
editText.BackPressed += (s, e) => 
{
    // <insert code here>
};

Trong khi đó chỉ là một ví dụ đơn giản, tôi khuyên bạn không nên sử dụng các phương thức ẩn danh trong các trình xử lý sự kiện, vì những phương pháp này tạo ra rò rỉ bộ nhớ và nhiều người sử dụng các ví dụ được tìm thấy ở đây và chạy với chúng mà không nhận ra điều này. Nguồn: docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/…
Jared

0

hideSoftInputFromWindow trả về true khi đóng bàn phím sử dụng giá trị của nó để phát hiện đóng bàn phím trong android

InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);

        if (imm.hideSoftInputFromWindow(findFocus().getWindowToken(),
                InputMethodManager.HIDE_NOT_ALWAYS)) {
            //keyboard is closed now do what you need here
        }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.