Android 从软键盘截取后退按钮

Android 从软键盘截取后退按钮,android,Android,我有几个输入字段的活动。活动开始时,显示软键盘。当按下后退按钮时,软键盘关闭,要关闭活动,我需要再次按下后退按钮 所以问题是:是否有可能在不创建自定义InputMethodService的情况下,截取后退按钮关闭软键盘并在一次按下后退按钮后完成活动 另外,我知道在其他情况下如何截取后退按钮:onKeyDown()或onBackPressed()但在这种情况下不起作用:只截取第二次按下后退按钮。您如何显示软键盘 如果您使用的是InputMethodManager.showSoftInput(),则

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

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


另外,我知道在其他情况下如何截取后退按钮:
onKeyDown()
onBackPressed()
但在这种情况下不起作用:只截取第二次按下后退按钮。

您如何显示软键盘

如果您使用的是
InputMethodManager.showSoftInput()
,则可以尝试传入
ResultReceiver
并实现
onReceiveResult()
来处理
结果\u隐藏


在您的BackPressed实现()中尝试以下代码:


我建议你看一看@

我也有同样的问题,但通过截取后退键来解决。在我的例子中(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();
}

是的,完全可以显示和隐藏键盘,并截获对后退按钮的呼叫。这是一点额外的工作,正如前面提到的,在API中没有直接的方法可以做到这一点。关键是覆盖布局中的布尔dispatchKeyEventPreIme(KeyEvent)。我们所做的就是创建我们的布局。我选择RelativeLayout,因为它是我活动的基础

<?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">
显然,
initInputField()
函数设置输入字段。它还启用enter键来执行功能(在我的例子中是搜索)

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

private void hideKeyboard() {
    InputMethodManager imm = (InputMethodManager) 
        getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(mInputField.getWindowToken(), 0);
}
无论如何,这是我对相对论的改写

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,您将看到这个想法的来源。

我发现,覆盖Layout类的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);
    }
}

使用
onKeyPreIme(int-keyCode,KeyEvent事件)
方法并检查
KeyEvent.keyCode\u BACK
事件。它非常简单,没有进行任何花哨的编码。

我成功地覆盖了dispatchKeyEvent

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
        finish();
        return true;
    }
    return super.dispatchKeyEvent(event);
}
它隐藏键盘并完成活动。

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

最初,您必须创建扩展EditText的自定义编辑文本。然后您必须实现onKeyPreIme方法,该方法控制KeyEvent.KEYCODE\u BACK。在此之后,一次回压足以解决您的问题。这个解决方案对我非常有效

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

@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)
}
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        (view as BazingaLayout).activity = activity
        super.onViewCreated(view, savedInstanceState)
}
}

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>
这是我的解的变体

我需要知道当键盘显示时,硬件或手势后退按钮何时被按下,这样我才能做出反应并显示之前在任何编辑文本视图收到焦点时隐藏的按钮

  • 首先声明您需要的回调接口
  • 然后使用ime前键事件的侦听器创建自定义视图
  • 使用自定义布局视图包装布局

  • 听起来不错,但是我的手机和仿真器上没有显示键盘,所以我用
    toggleSoftInput()。调用onReceiveResult()时会显示结果,\u,但以后不会再显示结果。\u HIDDENI我已经尝试过了,但仍然需要按两次back按钮。据我所知,第一次点击被软键盘截获,因此
    onBackPressed()
    不起作用。直到第二个press程序落入
    onBackPressed()
    @Sergey Glotov之后,我才尝试了很多关于这个问题的建议&最终找到了一个不太好的解决方案,我必须实现自己的软键盘。但我希望android社区很快会拿出一个更好的解决方案。你能给我展示一下这个布局的xml吗?我正在尝试检查这种方法,我的custum布局中有surface视图,但它没有截取任何内容,基本上,自定义视图是xml中的周围元素<代码>如果我们只是扩展单个视图并覆盖它的dispatchKeyEventPreIme(),它也可以工作。它不需要扩展布局本身。这种方法对我很有效。我扩展了我感兴趣的布局,然后传入了一个回调接口,而不是对活动的引用。这非常有效,并且不太难实现(尽管看起来有点吓人)。非常感谢您,我只是希望我有一种方法可以在旧版本的Android上使用类似的东西。只需将getContext()转换为Activity即可简化代码。当然,前提是布局的上下文是相关活动。但是我不知道这是否是不可能的。静态的MSearchavity——一个问题让它闻起来有点不对劲。如果在布局中有两个呢?改用非静态变量。效果很好。但是,我认为最好使用本地静态接口,并从内部活动设置它的实现,而不是使用静态活动…这似乎只适用于某些设备,如HTC Desire。不适用于三星note 2。当软键盘启动时,不会调用dispatchKeyEvent()。好吧,我用Nexus 6 Emulator(Lollipop 5.0)->工作了,Note 4(CM12 Lollipop 5.0.2)->工作了,不是最好的解决方案,但无论如何。。谢谢:)按钮
    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);
       }
    }
    
    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)
    }
    
    <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)
    }
    
    interface KeyboardEventListener {
       fun onKeyBoardDismissedIme()
    }
    
    class KeyboardAwareConstraintLayout(context: Context, attrs: AttributeSet) :
        ConstraintLayout(context, attrs) {
    
        var listener: KeyboardEventListener? = null
    
        override fun dispatchKeyEventPreIme(event: KeyEvent?): Boolean {
            val imm: InputMethodManager =
                context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            if (imm.isActive && event?.keyCode == KeyEvent.KEYCODE_BACK) {
                listener?.onKeyBoardDismissedIme()
            }
            return super.dispatchKeyEventPreIme(event)
        }
    }
    
    <com.package_name.KeyboardAwareLayout 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:id="@+id/keyBoardAwareLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
    {Your layout children here}    
    
    </com.package_name.KeyboardAwareLayout>
    
    
    class MyFragment : Fragment, KeyboardEventListener {
    
        // TODO: Setup some viewbinding to retrieve the view reference
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            binding.keyBoardAwareLayout.listener = this
        }
    
        override fun onKeyBoardDismissedIme() {
            // TODO: React to keyboard hidden with back button
        }
    }