Kullanıcının sanal klavyeyi ne zaman kapattığını algılama


114

Görünümümde bir Metni Düzenle widget'ım var. Kullanıcı Metni Düzenle parçacığını seçtiğinde, bazı talimatları görüntülerim ve yazılım klavyesi görünür.

Kullanıcının metin girişini ne zaman tamamladığını algılamak için bir OnEditorActionListener kullanıyorum ve klavyeyi kapatıyorum, talimatları gizleyip bazı eylemler gerçekleştiriyorum.

Benim sorunum, kullanıcının BACK tuşuna basarak klavyeyi kapatmasıdır. İşletim sistemi klavyeyi kapatıyor, ancak talimatlarım (gizlemem gereken) hala görünüyor.

OnKeyDown'u geçersiz kılmayı denedim, ancak klavyeyi kapatmak için GERİ düğmesi kullanıldığında bu çağrılmıyor gibi görünüyor.

EditText widget'ında bir OnKeyListener ayarlamayı denedim, ancak bu da çağrılmıyor gibi görünüyor.

Sanal klavyenin kapatıldığını nasıl tespit edebilirim?

Yanıtlar:


160

Bunu yapmanın bir yolunu biliyorum. EditText'i alt sınıflandırın ve uygulayın:

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

Özel görünümlerinizi nasıl kullanacağınıza ilişkin bir bağlantı (EditText alt sınıfına girdiğinizde): http://developer.android.com/guide/topics/ui/custom-components.html


2
Donanım klavyesi olan Android kullanıcılarından, bunu yapmanın tuşlara basmayı bir şekilde engellediğine dair raporlar alıyorum. Şu anda ek bilgim yok.
esilver

Birkaç çözüm arıyorum, bu açık ara en iyisi!
Friesgaard

11
Bekle bekle bekle, buna üçüncü kez baktım - süper çağrı yapmamalı onKeyPreImemı? Yoksa böyle olmamasının belirli bir nedeni var mı?
Erhannis

EditText'in alt sınıflara ayrılamadığı yerler dışında yararlı görünüyor (örneğin, bir SearchView'da). Bu, klavye kapatıldığında boşsa SearchView'ü gizlemeye çalışırken ortaya çıkan bir sorundur. Android insanlarının neden bu tür şeyler için sadece bazı güzel OSK API'leri sağlamadığını merak etmeliyim.
tbm

2
@tbm içinde benzer bir etki elde etmek için SearchView, lütfen stackoverflow.com/questions/9629313/… 'a
Cheok Yan Cheng

124

Jay, çözümün güzel! Teşekkürler :)

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

Bizim de tespit etmek istediğimiz belirli bir sebep var KeyEvent.ACTION_UPmı?
Cheok Yan Cheng

2
@CheokYanCheng, çünkü kullanıcı eyleminin normalde düğmeye basmaya başladığında değil, düğmeyi bırakırken etkili olması gerektiğidir.
jayeffkay

3
android.support.v7.widget.AppCompatEditTextRenklendirme için uzattığınızdan emin olun .
Sanvywell

Genişlet: AppCompatEditTextandroidx için
COYG

Harika! Yalnızca çözümünüzü doğrulamak için bir iyileştirme öneririm. OnKeyPreIme'deki argümanları dinleyiciye "olduğu gibi" geçirirdim, bu şekilde mantığınızı ihtiyacınız olan yerde farklı şekillerde uygulayabilirsiniz.
marcRDZ

17

Jay'in çözümünde super.onKeyPreIme () 'i arayarak küçük bir değişiklik yaptım:

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

Harika çözüm Jay, +1!


14

İşte klavyenin gösterilip gösterilmediğini algılamak için özel EditText'im

/**
 * 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

Şimdi 2019 ...
Bu yüzden Kotlin ile daha temiz bir çözüm yarattım

1. Bir uzantı işlevi oluşturun:

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. Geçiş dinleyicisinin olduğu yer:

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. Bunu herhangi bir Aktivitede bu kadar basit kullanın :

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

Kotlin çözümü için teşekkürler. Yine de uyguladığımda, dinleyiciyi bir klavye değişikliği için ve ayrıca başlangıçta birden çok kez çalıştırdığını fark ettim. Açık / açık değil durumunu saklamalıydım ve yalnızca değer gerçekten farklı olduğunda dinleyicileri çağırmalıydım.
RandomEngy

@RandomEngy bunu KeyboardToggleListener'da düzeltti.
Fark

4

Sadece Edittext'i genişleten bir sınıf oluşturun ve kodunuzda bu edittext'i kullanın, sadece özel edittext'de aşağıdaki yöntemi geçersiz kılmalısınız:

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

Klavyenin ne zaman açıldığını algılamanın bir yolu var mı?
powder366

3

İşte anahtar dinleyiciyle bir çözüm. Bunun neden işe yaradığı hakkında hiçbir fikrim yok, ancak OnKeyListener, özel EditText'inizde yalnızca onKeyPreIme'i geçersiz kılarsanız çalışır.

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

@ Olivier_sdg yanıtını kullanarak, ancak Kotlin'e dönüştürüldü:

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

}

Kullanımı:

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

2

Aynısını Xamarin'de yapmak isteyenler için, en iyi yanıtlardan bazılarını biraz farklı olduğu için çevirdim. Bir özünü oluşturdu burada ama özetleme, bir özel EditText ve engelleme oluşturmak OnKeyPreImeşöyle:

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

... ve sonra görünümde ...

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

Bu sadece basit bir örnek olsa da, olay işleyicilerinde anonim yöntemler kullanılmamasını tavsiye ederim, çünkü bunlar bellek sızıntıları yaratıyor ve birçok kişi burada bulunan örnekleri kullanıyor ve farkında olmadan onlarla çalışıyor. Kaynak: docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/…
Jared,

0

hideSoftInputFromWindow klavye kapandığında doğru döndürür, android'de klavye yakınlığını algılamak için değerini kullanır

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
        }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.