Mendeteksi saat pengguna menutup keyboard lunak

114

Saya memiliki widget EditText dalam tampilan saya. Ketika pengguna memilih widget EditText, saya menampilkan beberapa instruksi dan keyboard lunak muncul.

Saya menggunakan OnEditorActionListener untuk mendeteksi ketika pengguna telah menyelesaikan entri teks dan saya menutup keyboard, menyembunyikan instruksi dan melakukan beberapa tindakan.

Masalah saya adalah ketika pengguna menutup keyboard dengan menekan tombol BACK. OS menutup keyboard, tetapi instruksi saya (yang perlu saya sembunyikan) masih terlihat.

Saya sudah mencoba mengesampingkan OnKeyDown, tetapi sepertinya itu tidak dipanggil saat tombol KEMBALI digunakan untuk menutup keyboard.

Saya sudah mencoba menyetel OnKeyListener di widget EditText, tetapi tampaknya itu juga tidak dipanggil.

Bagaimana saya bisa mendeteksi ketika soft keyboard sedang ditutup?

deSelby
sumber

Jawaban:

160

Saya tahu cara untuk melakukan ini. Buat subkelas EditText dan implementasikan:

@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);
}

Berikut ini tautan tentang cara menggunakan tampilan ubahsuaian Anda (untuk saat Anda membuat subkelas EditText): http://developer.android.com/guide/topics/ui/custom-components.html

Jay
sumber
2
Saya mendapatkan laporan dari pengguna Android dengan keyboard Hardware yang melakukan ini entah bagaimana mengganggu penekanan tombol. Saya tidak punya info tambahan untuk saat ini.
esilver
Saya telah mencari beberapa solusi, sejauh ini yang terbaik!
Friesgaard
10
Tunggu tunggu tunggu, saya baru saja melihat ini untuk ketiga kalinya - bukankah panggilan super harus dilakukan onKeyPreIme? Atau adakah alasan khusus untuk tidak melakukannya?
Erhannis
Tampak berguna, kecuali jika EditText tidak bisa disubkelaskan (misalnya dalam SearchView). Ini adalah masalah saat mencoba menyembunyikan SearchView jika kosong saat keyboard ditutup. Saya harus bertanya-tanya mengapa orang-orang Android tidak hanya menyediakan beberapa API OSK yang bagus untuk hal semacam ini.
tbm
2
@tbm Untuk mencapai efek yang sama di SearchView, silakan lihat stackoverflow.com/questions/9629313/…
Cheok Yan Cheng
124

Jay, solusimu bagus! terima kasih :)

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);
}
olivier_sdg
sumber
Adakah alasan khusus yang ingin kami deteksi KeyEvent.ACTION_UPjuga?
Cheok Yan Cheng
2
@CheokYanCheng itu karena tindakan pengguna biasanya berlaku saat melepaskan tombol, dan bukan saat mulai menekannya.
jayeffkay
3
Pastikan untuk memperpanjang android.support.v7.widget.AppCompatEditTextuntuk pewarnaan.
Sanvywell
Perpanjang: AppCompatEditTextuntuk androidx
COYG
Bagus! Saya hanya menyarankan perbaikan untuk menghasilkan solusi Anda. Saya akan meneruskan argumen dari onKeyPreIme "sebagaimana adanya" kepada pendengar, dengan cara ini Anda dapat mengimplementasikan logika Anda dengan berbagai cara yang Anda butuhkan.
marcRDZ
17

Saya membuat sedikit perubahan pada solusi Jay dengan memanggil 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);
    }
};

Solusi luar biasa, Jay, +1!

Steelight
sumber
14

Berikut adalah EditText kustom saya untuk mendeteksi apakah keyboard ditampilkan atau tidak

/**
 * 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);
    }
}
Artis Terbaik
sumber
8

Sekarang tahun 2019 ...
Jadi saya membuat solusi yang lebih rapi dengan Kotlin

1. Buat fungsi ekstensi:

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. Di mana toggle listener berada:

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. Gunakan dalam Aktivitas apa pun sesederhana ini :

addKeyboardToggleListener {shown ->
          // hurray! Now you know when the keyboard is shown and hidden!!
      }
Leo Droidcoder
sumber
Terima kasih atas solusi Kotlin. Meskipun ketika saya menerapkannya, saya perhatikan bahwa itu mengaktifkan pendengar beberapa kali untuk satu perubahan keyboard, dan juga saat startup. Saya harus menyimpan status buka / tidak terbuka dan hanya memanggil pendengar ketika nilainya sebenarnya berbeda.
RandomEngy
@RandomEngy memperbaikinya di KeyboardToggleListener. Terima kasih telah memperhatikan
Leo Droidcoder
4

Cukup buat kelas yang memperluas Edittext dan gunakan teks editan itu dalam kode Anda, Anda harus mengganti metode berikut dalam teks editan khusus:

@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;}
farhad.kargaran
sumber
Adakah cara untuk mendeteksi saat keyboard terbuka?
bedak366
3

Inilah solusi dengan pendengar utama. Saya tidak tahu mengapa ini berfungsi tetapi OnKeyListener berfungsi jika Anda hanya mengganti onKeyPreIme pada EditText kustom Anda.

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);
    }
Rubin Yoo
sumber
3

Menggunakan jawaban @ olivier_sdg, tetapi dikonversi ke 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)
    }

}

Pemakaian:

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

Bagi siapa pun yang ingin melakukan hal yang sama di Xamarin, saya telah menerjemahkan beberapa jawaban teratas karena sedikit berbeda. Saya membuat intinya di sini tetapi meringkas, Anda membuat EditText kustom dan menimpa OnKeyPreImeseperti:

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);
    }
}

... dan kemudian dalam tampilan ...

editText = FindViewById<CustomEditText>(Resource.Id.MyEditText);
editText.BackPressed += (s, e) => 
{
    // <insert code here>
};
Steven Evers
sumber
Meskipun ini hanya contoh sederhana, saya akan merekomendasikan untuk tidak menggunakan metode anonim di event handler, karena ini membuat kebocoran memori, dan banyak orang menggunakan contoh yang ditemukan di sini dan menjalankannya tanpa menyadarinya. Sumber: docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/…
Jared
0

hideSoftInputFromWindow mengembalikan nilai true saat keyboard ditutup, gunakan nilainya untuk mendeteksi penutupan keyboard di 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
        }
Bali
sumber