Визначення, коли користувач відхилив м'яку клавіатуру


114

У мене на екрані віджет EditText. Коли користувач вибирає віджет EditText, я показую деякі вказівки і з’являється м'яка клавіатура.

Я використовую OnEditorActionListener для виявлення, коли користувач завершив введення тексту, і я відхиляю клавіатуру, приховую інструкції та виконую певні дії.

Моя проблема полягає в тому, що користувач відхиляє клавіатуру натисканням клавіші НАЗАД. ОС відхиляє клавіатуру, але мої вказівки (які мені потрібно приховати) все ще видно.

Я намагався змінити функцію OnKeyDown, але це, схоже, не називається, коли кнопка НАЗАД використовується для відхилення клавіатури.

Я спробував встановити OnKeyListener на віджет EditText, але, схоже, теж не називався.

Як я можу виявити, коли м'яка клавіатура відхиляється?

Відповіді:


160

Я знаю спосіб це зробити. Підклас EditText та реалізація:

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

Ось посилання про те, як використовувати власні власні представлення даних (для підкласу EditText): http://developer.android.com/guide/topics/ui/custom-components.html


2
Я отримую звіти від користувачів Android за допомогою апаратних клавіатур, що це якось заважає натисканням клавіш. Наразі у мене немає додаткової інформації.
esilver

Я шукав кілька рішень, це, безумовно, найкраще!
Friesgaard

10
Зачекайте, почекайте, я просто подивився на це третій раз - чи не повинен бути супердзвінок onKeyPreIme? Або є певна причина, щоб це не було так?
Ерханніс

Здається корисним, за винятком випадків, коли EditText не можна підкласифікувати (наприклад, у SearchView). Це проблема при спробі приховати SearchView, якщо порожній при відхиленні клавіатури. Мені потрібно дивуватися, чому андроїд люди не просто пропонують деякі приємні API-програми OSK для подібних речей.
tbm

2
@tbm Для досягнення подібного ефекту в SearchView, будь ласка , зверніться до stackoverflow.com/questions/9629313 / ...
Чеок Yan Cheng

124

Джей, ваше рішення добре! Дякую :)

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

Якась конкретна причина, яку ми хочемо виявити KeyEvent.ACTION_UPтакож?
Чак Ян Чен

2
@CheokYanCheng це тому, що дії користувача зазвичай повинні діяти під час відпускання кнопки, а не при натисканні на неї.
jayeffkay

3
Не забудьте продовжити android.support.v7.widget.AppCompatEditTextдля тонування.
Санвівелл

Розширення: AppCompatEditTextдля androidx
COYG

Чудово! Я б запропонував лише покращення, щоб просто узагальнити ваше рішення. Я передавав би аргументи від onKeyPreIme "як є" слухачеві, таким чином ви можете реалізувати свою логіку різними способами там, де вам потрібно.
marcRDZ

17

Я змінив рішення Джея, зателефонувавши на 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);
    }
};

Чудове рішення, Джей, +1!


14

Ось мій користувацький EditText, щоб визначити, відображається чи ні клавіатура

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

Зараз 2019 рік ...
Тож я створив більш акуратне рішення з Котліном

1.Створіть функцію розширення:

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. Де перемикач слухачів:

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. Використовуйте його в будь-якій діяльності настільки просто :

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

Дякую за рішення Котліна. Хоча, коли я реалізував це, я помітив, що він запускає слухача кілька разів за одну зміну клавіатури, а також при запуску. Мені довелося зберігати відкритий / не відкритий стан і лише викликати слухачів, коли значення насправді було іншим.
RandomEngy

@RandomEngy виправив це в KeyboardToggleListener. Дякуємо за те, що помітили
Leo Droidcoder

4

Просто створіть клас, який розширює Edittext, і використовуйте цей редакційний текст у своєму коді, вам слід просто замінити наступний метод у власному редакційному тексті:

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

Чи є спосіб виявити, коли клавіатура відкривається?
порошок366

3

Ось рішення з ключовим слухачем. Я не маю поняття, чому це працює, але OnKeyListener працює, якщо ви просто перекриваєте onKeyPreIme на вашому користувальницькому EditText.

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, але перетворений на Котлін:

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

}

Використання:

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

2

Для тих, хто хоче зробити те ж саме в Xamarin, я переклав кілька найкращих відповідей, оскільки це трохи інакше. Я створив тут суть, але підсумовуючи, ви створюєте користувацький EditText і переосмислюєте OnKeyPreImeтак:

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

... а потім на виду ...

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

Хоча це просто простий приклад, я рекомендую не використовувати анонімні методи в обробниках подій, оскільки вони створюють витоки пам'яті, і багато людей використовують приклади, знайдені тут, і працюють з ними, не усвідомлюючи цього. Джерело: docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/…
Джаред

0

hideSoftInputFromWindow повертає істину, коли клавіатура закривається, використовує її значення для виявлення закриття клавіатури в 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
        }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.