如何在Android上点击EditText外部后隐藏软键盘?

426

大家都知道隐藏键盘需要实现:

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

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

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

我尝试实现了一个触摸、点击、焦点监听器,但都没有成功。

我甚至尝试实现自己的滚动视图来拦截触摸事件,但我只能获得事件的坐标而无法获取所点击的视图。

是否有一种标准方法来解决这个问题?在iPhone上,这真的很容易。


我意识到ScrollView并不是问题所在,而是其中的标签。该视图是一个垂直布局,包含如下内容: TextView、EditText、TextView、EditText等等。而这些TextView会阻止EditText失去焦点并隐藏键盘。 - htafoya
你可以在这里找到 getFields() 的解决方案:https://dev59.com/e2sz5IYBdhLWcg3wlY-9 - Reto
键盘可以通过按回车键来关闭,因此我认为这是否值得努力是有问题的。 - gerrytan
5
我找到了这篇回答:https://dev59.com/7m445IYBdhLWcg3wfKcZ#28939113,是最好的一个。 - Loenix
49个回答

3

活动

 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
     ScreenUtils.hideKeyboard(this, findViewById(android.R.id.content).getWindowToken());
     return super.dispatchTouchEvent(ev);
 }

屏幕工具类

 public static void hideKeyboard(Context context, IBinder windowToken) {
     InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
     imm.hideSoftInputFromWindow(windowToken, InputMethodManager.HIDE_NOT_ALWAYS);
 }

4
这段代码很简单,但有一个明显的问题:它会在任何地方被点击时关闭键盘。也就是说,如果你点击EditText的其他位置移动输入光标,它就会隐藏键盘,而且键盘会由系统再次弹出。 - Damn Vegetables

3

显示/隐藏软键盘的方法

InputMethodManager inputMethodManager = (InputMethodManager) currentActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
    if (isShow) {
        if (currentActivity.getCurrentFocus() == null) {
            inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
        } else {
            inputMethodManager.showSoftInput(currentActivity.getCurrentFocus(), InputMethodManager.SHOW_FORCED);    
        }

    } else {
        if (currentActivity.getCurrentFocus() == null) {
            inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_NOT_ALWAYS, 0);
        } else {
            inputMethodManager.hideSoftInputFromInputMethod(currentActivity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);    
        }

    }

我希望它们对你有所帮助


3

有一种更简单的方法,基于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;
                }
            });

1
就像我所说的并且记得的那样,只有在表单不在ScrollView内部时才能正常工作。 - htafoya
1
如果背景布局包含其他子布局,则这种方法效果不佳。 - AxeEffect
谢谢你提醒,onClick 不是唯一的选择。 - Harpreet

2
我已经这样做了:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
   View view = getCurrentFocus();
   if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
            int scrcoords[] = new int[2];
            view.getLocationOnScreen(scrcoords);
            float x = ev.getRawX() + view.getLeft() - scrcoords[0];
            float y = ev.getRawY() + view.getTop() - scrcoords[1];
            if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
                hideKeyboard(this);
        }
    return super.dispatchTouchEvent(ev);
}

隐藏键盘代码:

public static void hideKeyboard(Activity act) {
    if(act!=null)
      ((InputMethodManager)act.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((act.getWindow().getDecorView().getApplicationWindowToken()), 0);
  }

完成


2

这可能有些老了,但我通过实现自定义类来使其工作。

public class DismissKeyboardListener implements OnClickListener {

    Activity mAct;

    public DismissKeyboardListener(Activity act) {
        this.mAct = act;
    }

    @Override
    public void onClick(View v) {
        if ( v instanceof ViewGroup ) {
            hideSoftKeyboard( this.mAct );
        }
    }       
}

public void hideSoftKeyboard(Activity activity) {
        InputMethodManager imm = (InputMethodManager)
        getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
}

最佳实践是创建一个帮助类,每个容器相对或线性布局都应该实现这个类。

**** 注意只有主容器应该实现此类(为了优化)****

并像这样实现:

Parent.setOnClickListener( new DismissKeyboardListener(this) ); 

关键字“this”是用于Activity的。如果你在Fragment上使用,就需要像这样使用getActivity();

--- 如果有帮助,请点赞... --- 致敬 Ralph ---


2

只需在类中添加以下代码 @Override

public boolean dispatchTouchEvent(MotionEvent ev) {
    View view = getCurrentFocus();
    if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
        int scrcoords[] = new int[2];
        view.getLocationOnScreen(scrcoords);
        float x = ev.getRawX() + view.getLeft() - scrcoords[0];
        float y = ev.getRawY() + view.getTop() - scrcoords[1];
        if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
            ((InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((this.getWindow().getDecorView().getApplicationWindowToken()), 0);
    }
    return super.dispatchTouchEvent(ev);
}

4
虽然这段话回答了问题,但最好解释一下答案的关键部分,可能还要说明一下原始提问者代码中存在的问题。 - pirho
是的 @pirho,我也同意你的观点,Haseeb需要专注于给出适当的答案。 - Dilip
3
你知道你也可以赞同你同意的评论吗?这只是为了保持评论区干净,不会有太多实际上表达相同观点的评论。 - pirho

2

我已经完善了这种方法,将以下代码放入某个UI实用程序类(最好是,但不一定)中,以便可以从所有Activity或Fragment类中访问它来达到其目的。

public static void serachAndHideSoftKeybordFromView(View view, final Activity act) {
    if(!(view instanceof EditText)) {
        view.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                hideSoftKeyboard(act);
                return false;
            }
        });
    }
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View nextViewInHierarchy = ((ViewGroup) view).getChildAt(i);
            serachAndHideSoftKeybordFromView(nextViewInHierarchy, act);
        }
    }
}
public static void hideSoftKeyboard (Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}

例如,如果你需要从活动中调用它,请按照以下方式进行调用;
UIutils.serachAndHideSoftKeybordFromView(findViewById(android.R.id.content), YourActivityName.this);

注意

findViewById(android.R.id.content)

这将为我们提供当前组的根视图(您不应该在根视图上设置id)。

干杯 :)


2
为了解决这个问题,你需要首先使用 Edittext 的 setOnFocusChangeListener。
edittext.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    Log.d("focus", "focus loosed");
                    // Do whatever you want here
                } else {
                    Log.d("focus", "focused");
                }
            }
        });

然后你需要在包含该Edittext的活动中覆盖dispatchTouchEvent,请参见以下代码。
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        View v = getCurrentFocus();
        if ( v instanceof EditText) {
            Rect outRect = new Rect();
            v.getGlobalVisibleRect(outRect);
            if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
                Log.d("focus", "touchevent");
                v.clearFocus();
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            }
        }
    }
    return super.dispatchTouchEvent(event);
}

现在的情况是,当用户点击外部区域时,将首先调用dispatchTouchEvent函数,然后会从editext中清除焦点,接着会调用OnFocusChangeListener函数来通知焦点已经改变,你可以在这里执行任何你想做的事情,希望它能起作用。


2

2

我使用了 Fernando Camarago 的方案,稍微做了些变通就使其工作起来了。在我的 onCreate 方法中,我将一个单一的 onTouchListener 附加到根视图,但是将视图作为参数而不是活动。

        findViewById(android.R.id.content).setOnTouchListener(new OnTouchListener() {           
        public boolean onTouch(View v, MotionEvent event) {
            Utils.hideSoftKeyboard(v);
            return false;
        }
    });

在一个独立的 Utils 类中,有一个名为...的函数。
    public static void hideSoftKeyboard(View v) {
    InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接