ChatGPT解决这个技术问题 Extra ChatGPT

单击EditText外部后如何在android上隐藏软键盘?

好的,每个人都知道要隐藏键盘,您需要实现:

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

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

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

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

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

有没有标准的方法来做到这一点?在 iPhone 中,这真的很容易。

好吧,我意识到滚动视图并不是真正的问题,而是那里的标签。该视图是一个垂直布局,其中包含:TextView、EditText、TextView、EditText 等。并且 textViews 不会让 edittext 失去焦点并隐藏键盘
您可以在此处找到 getFields() 的解决方案:stackoverflow.com/questions/7790487/…
键盘可以通过按返回按钮关闭,所以我想说这是否值得努力
我找到了这个答案:stackoverflow.com/a/28939113/2610855 最好的一个。

G
GustavoStingelin

以下代码段只是隐藏了键盘:

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)

最棘手的部分是何时调用它。您可以编写一个遍历 Activity 中每个 View 的方法,并检查它是否是一个 instanceof 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 并将此方法放入,并使您的应用程序中的所有其他活动都扩展此活动并在 onCreate() 方法中调用它的 setupUI()

希望能帮助到你。

如果您使用超过 1 个活动,请为父布局定义公共 id,例如 <RelativeLayout android:id="@+id/main_parent"> ... </RelativeLayout>

然后从 Activity 扩展一个类并在其 OnResume() 中定义 setupUI(findViewById(R.id.main_parent)) 并扩展此类而不是 ``Activity in your program

这是上述函数的 Kotlin 版本:

@file:JvmName("KeyboardUtils")

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

我还没有测试过自己,但看起来它会起作用,并且因为它有很高的评价,我会改变这个接受的答案。
应该不会很辛苦吧?我现在没有 Android 编程,所以如果我错了,请纠正我。您可以随时以某种方式跟踪聚焦的 EditText,并在 OnTouchEvent 期间请求它失去焦点?
不确定是否有其他人遇到过这个问题,但是如果没有任何焦点,这会导致应用程序在您调用 hideSoftKeyboard 时崩溃。您可以通过用 if(activity.getCurrentFocus() != null) {...} 包围方法的第二行来解决此问题
这种方法的问题在于它假定所有其他视图都不需要为它们设置 OnTouchListener。您可以将 ViewGroup.onInterceptTouchEvent(MotionEvent) 中的逻辑设置为根视图。
当我单击保持键盘打开的其他控件时不起作用
v
vida

您可以通过执行以下步骤来实现此目的:

通过添加以下属性使父视图(活动的内容视图)可点击和聚焦 android:clickable="true" android:focusableInTouchMode="true" 实现 hideKeyboard() 方法 public void hideKeyboard(View view) { InputMethodManager inputMethodManager = (InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);最后,设置编辑文本的 onFocusChangeListener。 edittext.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { hideKeyboard(v); } } });

正如以下评论之一所指出的,如果父视图是 ScrollView,这可能不起作用。对于这种情况,可以在 ScrollView 正下方的视图上添加 clickable 和 focusableInTouchMode。


在我看来,这是正确的答案。更少的代码,没有不必要的迭代......
我非常喜欢这个答案。需要注意的一点是,将 clickablefocusableInTouchMode 添加到我的根 ScrollView 元素时,这对我不起作用。我必须添加到我的 EditText 的直接父级,即 LinearLayout
非常适合我。但是,如果您有两个 edittext 小部件,则需要确保正确处理它们的 onfocus,否则您将不必要地切换键盘隐藏。
@MarkaA我对此没有任何问题,当我单击另一个EditText时,键盘会停留,如果单击背景它应该会隐藏。我在 onFocusChange 确实有一些其他处理,只是在 EditText 有焦点时更改它的背景,没什么特别的。
@Shrikant - 我也看到多个编辑文本闪烁。我只是在父级而不是每个编辑文本上设置 onFocusChangeListener,并将条件切换为 if (hasFocus) { hideKeyboard(v);没有注意到切换 btwn 编辑文本的闪烁。
R
RedGlyph

只需在 Activity 中覆盖以下代码

 @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (getCurrentFocus() != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
    return super.dispatchTouchEvent(ev);
}

更新:

如果有人需要这个答案的 Kotlin 版本:

override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
    if (currentFocus != null) {
        val imm = activity!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(activity!!.currentFocus!!.windowToken, 0)
    }
    return super.dispatchTouchEvent(ev)
}

简单的解决方案,添加活动,它也会处理片段
另一种解决方案是创建 BaseActivity 并在所有活动中扩展它
出色的解决方案
关闭键盘时,您使我免于闪烁屏幕。竖起大拇指!
不错,但好得令人难以置信:简单,很短,而且有效......唉,有一个问题:当键盘显示时,每次我们触摸询问键盘的 EditText 时,它都会下降并自动起来。
G
Gustavo Barbosa

我发现接受的答案有点复杂。

这是我的解决方案。在主布局中添加 OnTouchListener,即:

findViewById(R.id.mainLayout).setOnTouchListener(this)

并将以下代码放入 onTouch 方法中。

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

这样您就不必遍历所有视图。


@roepit - 我得到一个 classCastexception 来尝试将布局转换为视图。我错过了什么吗?
您可以在某处引用您的代码吗?当我无法查看您的布局和活动/片段等代码时,我无法判断出了什么问题。
那里的最佳答案,仍然试图围绕它是如何工作的。
如果您单击应用程序的标题栏,这会起作用吗?
这非常适合隐藏键盘!请注意,这实际上并没有使 EditText 失去焦点,它只是隐藏了键盘。要同时取消对 EditText 的关注,请将 android:onClick="stealFocusFromEditTexts" 添加到父视图的 xml,然后将 public void stealFocusFromEditTexts(View view) {} 添加到其活动。 on-click 方法不需要做任何事情,它只需要存在父视图才能成为焦点/可选择,这是从子 EditText 窃取焦点所必需的
J
JJD

我得到了另一种隐藏键盘的解决方案:

InputMethodManager imm = (InputMethodManager) getSystemService(
    Activity.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);

这里在 showFlag 的位置传递 HIDE_IMPLICIT_ONLY,在 hiddenFlag 的位置传递 0。它将强制关闭软键盘。


谢谢它的工作.....首先我已经尝试过,但是当我从对话框editext文本和关闭对话框中获取价值时它不起作用......
谢谢,这只是工作,它比上面的另一个更干净! +1
它按预期工作,谢谢您的解决方案+1
对我来说就像一个魅力。 +1 优雅的解决方案。
对不起,但是这个方法是切换的,所以如果键盘状态已经关闭,它会显示键盘
P
Phil

一个更Kotlin &使用 TextInputEditTextMaterial Design 方式(这种方式也与 EditTextView 兼容)...

1.通过添加以下属性使父视图(您的活动/片段的内容视图)可点击和可聚焦

android:focusable="true"
android:focusableInTouchMode="true"
android:clickable="true"

2.为所有视图创建一个扩展名(例如在 ViewExtension.kt 文件中):

fun View.hideKeyboard(){
    val inputMethodManager = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
    inputMethodManager.hideSoftInputFromWindow(this.windowToken, 0)
}

3.创建继承TextInputEditText的BaseTextInputEditText。实现 onFocusChanged 方法以在视图未聚焦时隐藏键盘:

class BaseTextInputEditText(context: Context?, attrs: AttributeSet?) : TextInputEditText(context, attrs){
    override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect)
        if (!focused) this.hideKeyboard()
    }
}

4.只需在您的 XML 中调用您全新的自定义视图:

<android.support.design.widget.TextInputLayout
        android:id="@+id/textInputLayout"
        ...>

        <com.your_package.BaseTextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            ... />

    </android.support.design.widget.TextInputLayout> 

就这样。无需修改您的控制器(片段或活动)来处理这种重复的情况。


S
Sergey Glotov

好吧,我设法在一定程度上解决了这个问题,我在我的活动中覆盖了 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() 方法上初始化,例如:

sFields = new EditText[] {mUserField, mPasswordField};

它并不完美,拖动事件时间仅基于启发式,因此有时在执行长 clics 时它不会隐藏,我还通过创建一个方法来获取每个视图的所有 editTexts 完成;否则,当单击其他 EditText 时,键盘会隐藏并显示。

尽管如此,还是欢迎更清洁和更短的解决方案


为了将来帮助其他人,您是否考虑在答案中编辑代码以包含您的 getFields() 方法?它不一定是精确的,只是一个示例,可能只是一些注释表明它返回一个 EditText 对象的数组。
S
Sergey Glotov

使用 OnFocusChangeListener

例如:

editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (!hasFocus) {
            hideKeyboard();
        }
    }
});

更新:您还可以在 Activity 中覆盖 onTouchEvent() 并检查触摸坐标。如果坐标在 EditText 之外,则隐藏键盘。


问题是当我单击标签或其他不可聚焦的视图时,edittext 不会失去焦点。
在这种情况下,我还有一个解决方案。我已经更新了答案。
onTouchEvent 调用了很多次,所以这也不是一个好习惯
S
Shohan Ahmed Sijan

在任何 Activity(或扩展 Activity 类)中覆盖 public boolean dispatchTouchEvent(MotionEvent event)

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    View view = getCurrentFocus();
    boolean ret = super.dispatchTouchEvent(event);

    if (view instanceof EditText) {
        View w = getCurrentFocus();
        int scrcoords[] = new int[2];
        w.getLocationOnScreen(scrcoords);
        float x = event.getRawX() + w.getLeft() - scrcoords[0];
        float y = event.getRawY() + w.getTop() - scrcoords[1];
        
        if (event.getAction() == MotionEvent.ACTION_UP 
 && (x < w.getLeft() || x >= w.getRight() 
 || y < w.getTop() || y > w.getBottom()) ) { 
            InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
        }
    }
 return ret;
}

科特林版本:

 override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        val ret = super.dispatchTouchEvent(ev)
        ev?.let { event ->
            if (event.action == MotionEvent.ACTION_UP) {
                currentFocus?.let { view ->
                    if (view is EditText) {
                        val touchCoordinates = IntArray(2)
                        view.getLocationOnScreen(touchCoordinates)
                        val x: Float = event.rawX + view.getLeft() - touchCoordinates[0]
                        val y: Float = event.rawY + view.getTop() - touchCoordinates[1]
                        //If the touch position is outside the EditText then we hide the keyboard
                        if (x < view.getLeft() || x >= view.getRight() || y < view.getTop() || y > view.getBottom()) {
                            val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                            imm.hideSoftInputFromWindow(view.windowToken, 0)
                            view.clearFocus()
                        }
                    }
                }
            }
        }
        return ret
    }

这就是你需要做的所有事情


这是我发现让它工作的最简单的方法。适用于多个 EditTexts 和一个 ScrollView
使用多个 EditText 对其进行了测试;有用!唯一的缺点是当你进行拖动时它也会隐藏起来。
J
Jishi Chen

我在 Activity 中实现了 dispatchTouchEvent 来做到这一点:

private EditText mEditText;
private Rect mRect = new Rect();
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int action = MotionEventCompat.getActionMasked(ev);

    int[] location = new int[2];
    mEditText.getLocationOnScreen(location);
    mRect.left = location[0];
    mRect.top = location[1];
    mRect.right = location[0] + mEditText.getWidth();
    mRect.bottom = location[1] + mEditText.getHeight();

    int x = (int) ev.getX();
    int y = (int) ev.getY();

    if (action == MotionEvent.ACTION_DOWN && !mRect.contains(x, y)) {
        InputMethodManager input = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        input.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
    }
    return super.dispatchTouchEvent(ev);
}

我测试了它,完美!


有效,但问题在于,如果我们有多个 EditText,那么我们也需要考虑这一点,但我喜欢你的回答 :-)
getActionMasked(ev) 已被弃用,所以现在使用: final int action = ev.getActionMasked();对于第一行。
F
Fernando Camargo

我修改了 Andre Luis IM 的解决方案,我实现了这个:

我创建了一个实用方法来隐藏软键盘,就像 Andre Luiz IM 所做的那样:

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

但是我没有为每个视图注册一个 OnTouchListener,这会导致性能很差,我只为根视图注册了 OnTouchListener。由于事件冒泡直到被消费(EditText是默认消费它的视图之一),如果它到达根视图,那是因为它没有被消费,所以我关闭了软键盘。

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

这对我来说似乎更安全。
这个和@AndyMc 的答案正是我所需要的。
f
fje

我知道这个线程已经很老了,正确的答案似乎是有效的,并且有很多可行的解决方案,但我认为下面所述的方法可能在效率和优雅方面有额外的好处。

我的所有活动都需要这种行为,所以我创建了一个继承自类 Activity 的类 CustomActivity 并“挂钩”了 dispatchTouchEvent 函数。主要有两个条件需要注意:

如果焦点未更改且有人在当前输入字段之外点击,则关闭 IME 如果焦点已更改且下一个焦点元素不是任何类型的输入字段的实例,则关闭 IME

这是我的结果:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if(ev.getAction() == MotionEvent.ACTION_UP) {
        final View view = getCurrentFocus();

        if(view != null) {
            final boolean consumed = super.dispatchTouchEvent(ev);

            final View viewTmp = getCurrentFocus();
            final View viewNew = viewTmp != null ? viewTmp : view;

            if(viewNew.equals(view)) {
                final Rect rect = new Rect();
                final int[] coordinates = new int[2];

                view.getLocationOnScreen(coordinates);

                rect.set(coordinates[0], coordinates[1], coordinates[0] + view.getWidth(), coordinates[1] + view.getHeight());

                final int x = (int) ev.getX();
                final int y = (int) ev.getY();

                if(rect.contains(x, y)) {
                    return consumed;
                }
            }
            else if(viewNew instanceof EditText || viewNew instanceof CustomEditText) {
                return consumed;
            }

            final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

            inputMethodManager.hideSoftInputFromWindow(viewNew.getWindowToken(), 0);

            viewNew.clearFocus();

            return consumed;
        }
    }       

    return super.dispatchTouchEvent(ev);
}

旁注:此外,我将这些属性分配给根视图,从而可以清楚地关注每个输入字段并防止输入字段获得对活动启动的关注(使内容视图成为“焦点捕捉器”):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    final View view = findViewById(R.id.content);

    view.setFocusable(true);
    view.setFocusableInTouchMode(true);
}

超级它工作正常谢谢!我为你的回答投了一票。
我认为这是复杂布局的最佳解决方案。但到目前为止我发现了 2 个缺点: 1. EditText 上下文菜单不可点击 - 任何点击它都会导致 EditText 失去焦点 2. 当我们的 EditText 位于视图底部并且我们长按它(选择单词)时,然后键盘显示我们的“点击点”在键盘上,而不是在 EditText 上 - 所以我们再次失去焦点:/
@sosite,我想我已经在回答中解决了这些限制;看一看。
@sosite 我正在使用类似的代码,上下文菜单没有问题。上下文菜单上的触摸事件不会分派给我的活动。
c
cyborg86pl

而不是遍历所有视图或覆盖 dispatchTouchEvent。

为什么不直接覆盖 Activity 的 onUserInteraction() 这将确保当用户在 EditText 之外点击时键盘会消失。

即使 EditText 在滚动视图内也可以工作。

@Override
public void onUserInteraction() {
    if (getCurrentFocus() != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
}

这是更好的答案
是的!这是一个超级干净的答案。如果你创建了一个所有其他活动都扩展的 AbstractActivity,你可以将它作为整个应用程序的默认行为。
在 kotline if (currentFocus != null && currentFocus !is EditText) 中给了我额外的里程
S
Sai

在 kotlin 中,我们可以执行以下操作。无需迭代所有视图。它也适用于片段。

override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
    currentFocus?.let {
        val imm: InputMethodManager = getSystemService(
            Context.INPUT_METHOD_SERVICE
        ) as (InputMethodManager)
        imm.hideSoftInputFromWindow(it.windowToken, 0)
    }
    return super.dispatchTouchEvent(ev)
}

这有效,但它有一个错误。例如,如果我想在 textview 中粘贴文本,键盘会隐藏然后显示。这有点烦人。
很好的答案对我来说很好,尽管正如乔治所说,并且在我的情况下,使用明文,例如在搜索小部件上,键盘隐藏然后再次显示。
T
Tass

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

我不明白计时器部分(不知道为什么需要测量停机时间?)

我不喜欢在每次视图更改时注册/取消注册所有 EditText(在复杂的层次结构中可能有很多视图更改和编辑文本)

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

@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 会使键盘隐藏并重新显示 - 在我的情况下,这是需要的,因为它表明您在两个输入组件之间切换。


这种方法在我的 FragmentActivity 即插即用方面效果最好。
谢谢,这是最好的方法!我还添加了对事件操作的检查: int action = ev.getActionMasked();如果(动作 == MotionEvent.ACTION_DOWN || 动作 == MotionEvent.ACTION_POINTER_DOWN){...}
谢谢 !你节省了我的时间..最佳答案。
C
Charles Woodson

恳求:我承认我没有影响力,但请认真对待我的回答。

问题:当点击离开键盘或用最少的代码编辑文本时关闭软键盘。

解决方案:称为 Butterknife 的外部库。

一条线解决方案:

@OnClick(R.id.activity_signup_layout) public void closeKeyboard() { ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); }

更具可读性的解决方案:

@OnClick(R.id.activity_signup_layout) 
public void closeKeyboard() {
        InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}

说明:将 OnClick 侦听器绑定到活动的 XML 布局父 ID,以便对布局的任何单击(而不是在编辑文本或键盘上)将运行将隐藏键盘的代码片段。

示例:如果您的布局文件是 R.layout.my_layout 并且您的布局 id 是 R.id.my_layout_id,那么您的 Butterknife 绑定调用应如下所示:

(@OnClick(R.id.my_layout_id) 
public void yourMethod {
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}

Butterknife 文档链接: http://jakewharton.github.io/butterknife/

Plug:Butterknife 将彻底改变您的 android 开发。考虑一下。

注意:不使用外部库 Butterknife 也可以达到相同的结果。如上所述,只需将 OnClickListener 设置为父布局。


太棒了,完美的解决方案!谢谢你。
s
swarnim dixit

它太简单了,只需通过以下代码使您最近的布局可点击成为焦点:

android:id="@+id/loginParentLayout"
android:clickable="true"
android:focusableInTouchMode="true"

然后为该布局编写一个方法和一个 OnClickListner ,这样当触摸最上面的布局时,它会调用一个方法,您将在其中编写代码以关闭键盘。以下是两者的代码; // 你必须在 OnCreate() 中写这个

 yourLayout.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View view) {
                    hideKeyboard(view);
                }
            });

从侦听器调用的方法:-

 public void hideKeyboard(View view) {
     InputMethodManager imm =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }

A
Andy Dennie

这是 fje 答案的另一个变体,它解决了 sosite 提出的问题。

这里的想法是在 Activity 的 dispatchTouchEvent 方法中同时处理向下和向上动作。在向下操作中,我们记下当前聚焦的视图(如果有)以及触摸是否在其中,将这两个信息都保存下来以备后用。

在 up 动作上,我们首先调度,以允许另一个视图潜在地获得焦点。如果在那之后,当前聚焦的视图是最初聚焦的视图,并且向下触摸在该视图内,那么我们让键盘保持打开状态。

如果当前聚焦的视图与最初聚焦的视图不同并且它是一个 EditText,那么我们也让键盘保持打开状态。

否则我们关闭它。

因此,总而言之,它的工作原理如下:

在当前聚焦的 EditText 内触摸时,键盘保持打开状态

当从一个焦点 EditText 移动到另一个 EditText 时,键盘保持打开状态(不关闭/重新打开)

当触摸当前聚焦的 EditText 之外的任何不是另一个 EditText 的地方时,键盘关闭

当长按 EditText 以调出上下文操作栏(使用剪切/复制/粘贴按钮)时,键盘保持打开状态,即使 UP 操作发生在焦点 EditText 之外(向下移动以为出租车)。但请注意,当您点击 CAB 中的按钮时,它将关闭键盘。这可能是可取的,也可能不是可取的;如果您想从一个字段剪切/复制并粘贴到另一个字段,那就是。如果您想粘贴回相同的 EditText,则不会。

当聚焦的 EditText 位于屏幕底部并且您长按某些文本以选择它时,EditText 会保持焦点,因此键盘会按您想要的方式打开,因为我们对“触摸在视图范围内”进行了检查向下动作,而不是向上动作。私有视图focusedViewOnActionDown;私有布尔触摸WasInsideFocusedView; @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN:focusedViewOnActionDown = getCurrentFocus(); if (focusedViewOnActionDown != null) { final Rect rect = new Rect();最终 int[] 坐标 = new int[2]; focusedViewOnActionDown.getLocationOnScreen(坐标); rect.set(坐标[0],坐标[1],坐标[0]+focusedViewOnActionDown.getWidth(),坐标[1]+focusedViewOnActionDown.getHeight());最终 int x = (int) ev.getX();最终 int y = (int) ev.getY(); touchWasInsideFocusedView = rect.contains(x, y); } 休息; case MotionEvent.ACTION_UP: if (focusedViewOnActionDown != null) { // 调度以允许新视图(可能)获得焦点 final boolean consume = super.dispatchTouchEvent(ev);最终视图 currentFocus = getCurrentFocus(); // 如果焦点仍然在原始视图上并且触摸在该视图内, // 保持键盘打开。否则,如果焦点现在在另一个视图上,并且该视图 // 是 EditText,也让键盘保持打开状态。 if (currentFocus.equals(focusedViewOnActionDown)) { if (touchWasInsideFocusedView) { 返回消耗; } } else if (currentFocus instanceof EditText) { 返回消耗; } // 触摸在最初聚焦的视图之外,而不是在另一个 EditText 内, // 所以关闭键盘 InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(focusedViewOnActionDown.getWindowToken(), 0); focusViewOnActionDown.clearFocus();消费回报; } 休息; } 返回 super.dispatchTouchEvent(ev); }


J
Joohay

对于这个简单的要求,我发现接受的答案有点复杂。这对我有用,没有任何故障。

findViewById(R.id.mainLayout).setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
            return false;
        }
    });

如果您使用 NestedScrollView 或复杂布局,请参阅接受的答案:stackoverflow.com/a/11656129/2914140。您应该知道其他容器可能会消耗触摸。
N
NullByte08

这对我来说是最简单的解决方案(并且由我制定)。

这是隐藏键盘的方法。

public void hideKeyboard(View view){
        if(!(view instanceof EditText)){
            InputMethodManager inputMethodManager=(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
        }
    }

现在,从 XML 文件的设计视图或在 XML 文件的文本视图中编写以下代码,将活动的父布局的 onclick 属性设置为上述方法 hideKeyboard

android:onClick="hideKeyboard"

A
Alex R. R.

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

正如我所说并且我记得,这仅在表单不在 ScrollView 内时才有效。
如果背景布局包含其他子布局,这不会很好地工作。
感谢您提醒 onClick 不是唯一的选择。
l
lalosoft

显示/隐藏软键盘的方法

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

    }

我希望它们有用


A
AndyMc

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

H
Hiren Patel

我已经这样做了:

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

完毕


S
Sudhanshu Gaur

要解决此问题,您必须首先使用该 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 将被调用,焦点已更改,现在在这里您可以做任何您想做的事情,希望它有效


U
Uzair

我已经改进了该方法,将以下代码放在一些 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);
}

然后说比如你需要从activity中调用它,调用方式如下;

UIutils.serachAndHideSoftKeybordFromView(findViewById(android.R.id.content), YourActivityName.this);

注意

findViewById(android.R.id.content)

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

干杯:)


J
JJD

尝试将 stateHidden 作为您的活动 windowSoftInputMode

http://developer.android.com/reference/android/R.attr.html#windowSoftInputMode

例如对于您的活动:

this.getWindow().setSoftInputMode(
    WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

J
Jean-François Corbett

活动

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

这段代码很简单,但它有一个明显的问题:当任何地方被触摸时它都会关闭键盘。也就是说,如果你点击 EditText 的不同位置来移动输入光标,它会隐藏键盘,然后系统会再次弹出键盘。
H
Haseeb Javed

只需在 @Overide 类中添加此代码

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

虽然这可能会回答问题,但最好解释答案的基本部分以及 OP 代码可能存在的问题。
是的@pirho 我也同意你的观点,Haseeb 需要集中精力给出正确的答案。
@Dilip 你知道你也可以投票赞成你同意的评论吗?这只是为了保持评论部分的清洁,以免有很多评论实际上具有相同的意义。
V
Vickel

您可以实现 View.onClickListener 并覆盖 onClick 方法并将此 onclicklistener 设置为布局

ConstraintLayout constraintLayout;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        constraintLayout = findViewById(R.id.layout);
        constraintLayout.setOnClickListener(this);
}
@Override
    public void onClick(View v) {
        if(v.getId()==R.id.layout){
            InputMethodManager inm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
            inm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
        }
    }