每个人都知道要隐藏一个键盘,你需要实现:

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

但这里的大问题是如何隐藏键盘时,用户触摸或选择任何其他地方,不是一个EditText或softKeyboard?

我尝试在我的父活动上使用onTouchEvent(),但只有当用户在任何其他视图之外触摸并且没有滚动视图时才有效。

我试图实现一个触摸,点击,焦点监听器,但没有任何成功。

我甚至尝试实现我自己的滚动视图来拦截触摸事件,但我只能得到事件的坐标,而不是视图被单击。

有标准的方法来做这件事吗?在iPhone中,这非常简单。


当前回答

好吧,我设法某种程度上解决了这个问题,我在我的活动上覆盖了dispatchTouchEvent,在那里我使用下面的隐藏键盘。

 /**
 * Called to process touch screen events. 
 */
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

    switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
            touchDownTime = SystemClock.elapsedRealtime();
            break;

        case MotionEvent.ACTION_UP:
            //to avoid drag events
            if (SystemClock.elapsedRealtime() - touchDownTime <= 150){  

                EditText[] textFields = this.getFields();
                if(textFields != null && textFields.length > 0){

                    boolean clickIsOutsideEditTexts = true;

                    for(EditText field : textFields){
                        if(isPointInsideView(ev.getRawX(), ev.getRawY(), field)){
                            clickIsOutsideEditTexts = false;
                            break;
                        }
                    }

                    if(clickIsOutsideEditTexts){
                        this.hideSoftKeyboard();
                    }               
                } else {
                    this.hideSoftKeyboard();
                }
            }
            break;
    }

    return super.dispatchTouchEvent(ev);
}

编辑:getFields()方法只是一个在视图中返回带有文本字段的数组的方法。为了避免每次触摸都创建这个数组,我创建了一个名为sFields的静态数组,它在getFields()方法中返回。该数组在onStart()方法上初始化,例如:

《新经》[英文]


它不是完美的,拖动事件时间只是基于启发式,所以有时它不会隐藏在执行长clics时,我也完成了创建一个方法来获得每个视图的所有editTexts;否则,当单击其他EditText时,键盘将隐藏和显示。

不过,更干净、更短的解决方案还是受欢迎的

其他回答

唯一有效的代码

private var viewHeight = 0

private fun setRootViewListener() {
    binding.root.apply {
        viewTreeObserver.addOnGlobalLayoutListener {
            viewHeight = height
        }
    }
}

override fun dispatchTouchEvent(event: MotionEvent): Boolean {
    currentFocus?.let {
        if (it is EditText && event.y < viewHeight - it.measuredHeight) {
            hideKeyboard(it)
        }
    }
    return super.dispatchTouchEvent(event)
}

下面的代码片段只是隐藏了键盘:

public static void hideSoftKeyboard(Activity activity) {
    InputMethodManager inputMethodManager = 
        (InputMethodManager) activity.getSystemService(
            Activity.INPUT_METHOD_SERVICE);
    if(inputMethodManager.isAcceptingText()){
        inputMethodManager.hideSoftInputFromWindow(
                activity.getCurrentFocus().getWindowToken(),
                0
        );
    }
}

你可以把它放在一个实用工具类中,或者如果你在一个活动中定义它,避免使用活动参数,或者调用hideSoftKeyboard(this)。

最棘手的部分是何时调用它。你可以写一个方法,遍历活动中的每个View,并检查它是否是EditText的实例,如果它没有注册setOnTouchListener到该组件,一切都将到位。如果你想知道如何做到这一点,实际上很简单。这就是你要做的,你写一个像下面这样的递归方法,事实上你可以用它来做任何事情,比如设置自定义字体等…这是方法

public void setupUI(View view) {

    // Set up touch listener for non-text box views to hide keyboard.
    if (!(view instanceof EditText)) {
        view.setOnTouchListener(new OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                hideSoftKeyboard(MyActivity.this);
                return false;
            }
        });
    }

    //If a layout container, iterate over children and seed recursion.
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View innerView = ((ViewGroup) view).getChildAt(i);
            setupUI(innerView);
        }
    }
}

这就是所有的,只是在你setContentView在你的活动之后调用这个方法。如果你想知道你要传递什么参数,它是父容器的id。给你的父容器分配一个id

<RelativeLayoutPanel android:id="@+id/parent">…< / RelativeLayout >

并调用setupUI(findViewById(R.id.parent)),仅此而已。

如果你想有效地使用它,你可以创建一个扩展的Activity并把这个方法放进去,并让应用程序中的所有其他活动扩展这个Activity并在onCreate()方法中调用它的setupUI()。

如果你使用了多个活动,请定义父布局的公共id <RelativeLayout android:id="@+id/main_parent">…< / RelativeLayout >

然后从Activity中扩展一个类,并在其OnResume()中定义setupUI(findViewById(R.id.main_parent)),并在程序中扩展这个类而不是' ' Activity


下面是上面函数的Kotlin版本:

@file:JvmName("KeyboardUtils")

fun Activity.hideSoftKeyboard() {
    currentFocus?.let {
        val inputMethodManager = ContextCompat.getSystemService(this, InputMethodManager::class.java)!!
        inputMethodManager.hideSoftInputFromWindow(it.windowToken, 0)
    }
}

有一个更简单的方法,基于iPhone同样的问题。简单地覆盖背景的布局触摸事件,其中包含编辑文本。只需在活动的OnCreate中使用这段代码(login_fondo是根布局):

    final LinearLayout llLogin = (LinearLayout)findViewById(R.id.login_fondo);
    llLogin.setOnTouchListener(
            new OnTouchListener()
            {
                @Override
                public boolean onTouch(View view, MotionEvent ev) {
                    InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(
                            android.content.Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(mActivity.getCurrentFocus().getWindowToken(), 0);
                    return false;
                }
            });
@Override
    public boolean onTouchEvent(MotionEvent event) {
        InputMethodManager imm = (InputMethodManager)getSystemService(Context.
                INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
        return true;
    }

我喜欢调用dispatchTouchEvent的方法由htafoya,但是:

我不理解定时器的部分(不知道为什么测量停机时间应该是必要的?) 我不喜欢注册/取消注册所有的EditTexts与每一个视图的变化(可能是相当多的视图变化和编辑文本在复杂的层次结构)

所以,我做了一个更简单的解决方案:

@Override
public boolean dispatchTouchEvent(final MotionEvent ev) {
    // all touch events close the keyboard before they are processed except EditText instances.
    // if focus is an EditText we need to check, if the touchevent was inside the focus editTexts
    final View currentFocus = getCurrentFocus();
    if (!(currentFocus instanceof EditText) || !isTouchInsideView(ev, currentFocus)) {
        ((InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE))
            .hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
    }
    return super.dispatchTouchEvent(ev);
}

/**
 * determine if the given motionevent is inside the given view.
 * 
 * @param ev
 *            the given view
 * @param currentFocus
 *            the motion event.
 * @return if the given motionevent is inside the given view
 */
private boolean isTouchInsideView(final MotionEvent ev, final View currentFocus) {
    final int[] loc = new int[2];
    currentFocus.getLocationOnScreen(loc);
    return ev.getRawX() > loc[0] && ev.getRawY() > loc[1] && ev.getRawX() < (loc[0] + currentFocus.getWidth())
        && ev.getRawY() < (loc[1] + currentFocus.getHeight());
}

但有一个缺点:

从一个EditText切换到另一个EditText使键盘隐藏并重新显示——在我的例子中,这是理想的方式,因为它显示您在两个输入组件之间切换。