从软键盘截取后退按钮


84

我有几个输入字段的活动。活动开始后,将显示软键盘。当按下后退按钮软键盘关闭并关闭活动时,我需要再按下一次后退按钮。

因此,问题是:是否可以拦截后退按钮以关闭软键盘并在不创建自定义功能的情况下按一下后退按钮来完成活动InputMethodService

PS我知道在其他情况下如何拦截后退按钮:onKeyDown()或者,onBackPressed()但在这种情况下不起作用:只有第二次按下后退按钮才能被拦截。

Answers:


76

是的,完全有可能显示和隐藏键盘并截获对后退按钮的呼叫。由于已经提到了API,因此没有直接的方法可以做一些额外的工作。关键是要boolean dispatchKeyEventPreIme(KeyEvent)在布局中覆盖。我们要做的是创建布局。我选择RelativeLayout,因为它是我的Activity的基础。

<?xml version="1.0" encoding="utf-8"?>
<com.michaelhradek.superapp.utilities.SearchLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.michaelhradek.superapp"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/white">

在活动中,我们设置输入字段并调用setActivity(...)函数。

private void initInputField() {
    mInputField = (EditText) findViewById(R.id.searchInput);        

    InputMethodManager imm = 
        (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 
            InputMethodManager.HIDE_IMPLICIT_ONLY);

    mInputField.setOnEditorActionListener(new OnEditorActionListener() {

        @Override
        public boolean onEditorAction(TextView v, int actionId,
                KeyEvent event) {
            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                performSearch();
                return true;
            }

            return false;
        }
    });

    // Let the layout know we are going to be overriding the back button
    SearchLayout.setSearchActivity(this);
}

显然,该initInputField()函数设置了输入字段。它还使Enter键能够执行功能(在我的情况下是搜索)。

@Override
public void onBackPressed() {
    // It's expensive, if running turn it off.
    DataHelper.cancelSearch();
    hideKeyboard();
    super.onBackPressed();
}

因此,当onBackPressed()在我们的布局中调用时,我们就可以做我们想做的任何事情,例如隐藏键盘:

private void hideKeyboard() {
    InputMethodManager imm = (InputMethodManager) 
        getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(mInputField.getWindowToken(), 0);
}

无论如何,这是我对RelativeLayout的替代。

package com.michaelhradek.superapp.utilities;

import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.RelativeLayout;

/**
 * The root element in the search bar layout. This is a custom view just to 
 * override the handling of the back button.
 * 
 */
public class SearchLayout extends RelativeLayout {

    private static final String TAG = "SearchLayout";

    private static Activity mSearchActivity;;

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

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

    public static void setSearchActivity(Activity searchActivity) {
        mSearchActivity = searchActivity;
    }

    /**
     * Overrides the handling of the back key to move back to the 
     * previous sources or dismiss the search dialog, instead of 
     * dismissing the input method.
     */
    @Override
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        Log.d(TAG, "dispatchKeyEventPreIme(" + event + ")");
        if (mSearchActivity != null && 
                    event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            KeyEvent.DispatcherState state = getKeyDispatcherState();
            if (state != null) {
                if (event.getAction() == KeyEvent.ACTION_DOWN
                        && event.getRepeatCount() == 0) {
                    state.startTracking(event, this);
                    return true;
                } else if (event.getAction() == KeyEvent.ACTION_UP
                        && !event.isCanceled() && state.isTracking(event)) {
                    mSearchActivity.onBackPressed();
                    return true;
                }
            }
        }

        return super.dispatchKeyEventPreIme(event);
    }
}

不幸的是,我不能承担所有的功劳。如果您在Android来源中查找快速SearchDialog框,则会看到该创意的来源。


2
这工作得很好,并且实施起来也不太困难(尽管看起来有些吓人)。
Cesar

非常感谢您,我只希望我有办法让类似的东西与旧版本的Android一起使用。
zidarsk8 2011年

3
只需将代码转换getContext()为即可简化代码Activity。只要布局的上下文是所讨论的活动,就可以了。但是我不知道是否不可能。
mxcl 2012年

3
静态mSearchActivity-闻起来有问题。如果您有两个这样的布局怎么办?请改用非静态变量。
指针为空

奇迹般有效。但是,我认为最好使用本地静态接口并从内部活动中设置其实现,而不要使用静态策略...
SpiralDev

80

onKeyDown()onBackPressed()在这种情况下不起作用。您必须使用onKeyPreIme

最初,您必须创建扩展EditText的自定义编辑文本。然后,您必须实现控制KeyEvent.KEYCODE_BACK的onKeyPreIme方法。此后,一按就足以解决您的问题。该解决方案非常适合我。

CustomEditText.java

public class CustomEditText extends EditText {

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

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            // User has pressed Back key. So hide the keyboard
            InputMethodManager mgr = (InputMethodManager)         

           getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            mgr.hideSoftInputFromWindow(this.getWindowToken(), 0);
            // TODO: Hide your view as you do it in your activity
        }
        return false;
}

在您的XML中

<com.YOURAPP.CustomEditText
     android:id="@+id/CEditText"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"/> 

在您的活动中

public class MainActivity extends Activity {
   private CustomEditText editText;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      editText = (CustomEditText) findViewById(R.id.CEditText);
   }
}

4
非常喜欢这个答案。还将它与一个片段一起使用(在一个甚至永远不会显示的搜索栏中),并建议覆盖editText,这很容易将这种行为封装为仅在必要时才会出现
Jawad

2
所有人注意:为此添加一个快速界面可以使生活变得更好,而且幸运的是它很容易实现
Jawad

如何在mainActivity.java中的onKeyPreIme()中调用方法?
阿比吉扬2015年

您无需在活动中调用onKeyPreIme。如果在活动中使用CustomEditText,则在按下键盘上的任何按钮时都会触发方法。
Alican Temel 2015年

1
我认为这是最好的答案,因为它更容易实现。实际上,您只需要复制“ CustomEditText”类(并在XML中重命名您的edittext),然后在// TODO区域中完成所需的操作即可。如果不想关闭活动,只需在TODO区域中添加“ return true”即可。谢谢 !
Chrysotribax

10

我发现,重写Layout Class的dispatchKeyEventPreIme方法也很好。只需将您的主活动设置为属性并启动预定义的方法。

public class LinearLayoutGradient extends LinearLayout {
    MainActivity a;

    public void setMainActivity(MainActivity a) {
        this.a = a;
    }

    @Override
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        if (a != null) {
            InputMethodManager imm = (InputMethodManager) a
                .getSystemService(Context.INPUT_METHOD_SERVICE);

            if (imm.isActive() && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                a.launchMethod;
            }
        }

        return super.dispatchKeyEventPreIme(event);
    }
}

您能告诉我这种布局的xml吗?我正在尝试检查这种方法,并且在cuscus布局中有曲面视图,但它没有拦截任何东西
ZZZ

基本上,定制视图是xml中的周围元素。<?xml version="1.0" encoding="utf-8"?> <view class="package.LinearLayoutGradient"...
Kirill Rakhman

如果我们仅扩展单个视图并覆盖它的dispatchKeyEventPreIme(),它也将起作用。不需要扩展布局本身。
faizal 2013年

7

我通过重写dispatchKeyEvent获得成功:

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
        finish();
        return true;
    }
    return super.dispatchKeyEvent(event);
}

隐藏键盘并完成活动。


这似乎仅适用于某些设备,例如HTC Desire。

5
在三星笔记2上不起作用。当软键盘启动时,不会调用dispatchKeyEvent()。
faizal

好吧,我尝试使用Nexus 6仿真器(Lollipop 5.0)->正常运行,而Note 4(CM12 Lollipop 5.0.2)->则不是这样,不是真正的最佳解决方案-但无论如何..谢谢:)
Martin Pfeffer

关闭键盘时会消耗按钮单击的时间
插槽

4

您如何显示软键盘?

如果您使用InputMethodManager.showSoftInput(),则可以尝试传入ResultReceiver和实施onReceiveResult()以处理RESULT_HIDDEN

http://developer.android.com/reference/android/view/inputmethod/InputMethodManager.html


听起来不错,但showSoftInput()我的手机和仿真器上没有显示键盘,所以我用了toggleSoftInput()
谢尔盖·格洛托夫

1
对我不起作用。用RESULT_SHOWN调用onReceiveResult(),但以后再用RESULT_HIDDEN调用
Yulia Rogovaya

2

我有同样的问题,但是通过拦截后退键来解决它。就我而言(HTC Desire,Android 2.2,应用程序API级别4),它关闭键盘并立即完成活动。不知道为什么这也不适合您:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        onBackPressed();
        return true;
    }
    return super.onKeyUp(keyCode, event);
}

/**
 * Called when the activity has detected the user's press of the back key
 */
private void onBackPressed() {
    Log.e(TAG, "back pressed");
    finish();
}

这在三星银河笔记2上不起作用。当软键盘可见且按下后退按钮时,不会触发onkeydown()或onbackpressed()
faizal

1

使用onKeyPreIme(int keyCode, KeyEvent event)方法并检查KeyEvent.KEYCODE_BACK事件。无需进行任何高级编码就非常简单。


0

在您的BackPressed实现中尝试此代码(android中的Block Back Button):

InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(myEditText.getWindowToken(), 0);

我建议您看看@关闭/隐藏Android软键盘


1
我已经尝试过了,但是仍然需要按两次返回按钮。据我了解,第一次点击被软键盘拦截,因此onBackPressed()无法正常工作。并且只有在第二次按下程序进入程序之后onBackPressed()
谢尔盖·格洛托夫

@Sergey Glotov我尝试了很多关于这个问题的建议,最后提出了一个不太好的解决方案,我必须实现自己的SoftKeyBoard。但是我希望android社区很快会提出更好的解决方案。
100rabh

0

我的@mhradek解决方案版本:

布局

class BazingaLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
    ConstraintLayout(context, attrs, defStyleAttr) {

var activity: Activity? = null

override fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
    activity?.let {
        if (event.keyCode == KeyEvent.KEYCODE_BACK) {
            val state = keyDispatcherState
            if (state != null) {
                if (event.action == KeyEvent.ACTION_DOWN
                    && event.repeatCount == 0) {
                    state.startTracking(event, this)
                    return true
                } else if (event.action == KeyEvent.ACTION_UP && !event.isCanceled && state.isTracking(event)) {
                    it.onBackPressed()
                    return true
                }
            }
        }
    }
    return super.dispatchKeyEventPreIme(event)
}

}

xml文件

<com... BazingaLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/grey">
 </com... BazingaLayout>

分段

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        (view as BazingaLayout).activity = activity
        super.onViewCreated(view, savedInstanceState)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.