如何在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个回答

2

您可以实现 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);
        }
    }

1
这是fje的答案稍作修改后的版本,基本上可以完美运行。
此版本使用ACTION_DOWN,因此执行滚动操作也会关闭键盘。除非您单击另一个EditText,否则它也不会传播事件。这意味着单击EditText之外的任何位置,甚至是其他可点击的区域,都会简单地关闭键盘。
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
    if(ev.getAction() == MotionEvent.ACTION_DOWN)
    {
        final View view = getCurrentFocus();

        if(view != null)
        {
            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))
                {
                    super.dispatchTouchEvent(ev);
                    return true;
                }
            }
            else if(viewNew instanceof EditText || viewNew instanceof CustomEditText)
            {
                super.dispatchTouchEvent(ev);
                return true;
            }

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

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

            viewNew.clearFocus();

            return true;
        }
    }
    return super.dispatchTouchEvent(ev);
}

这里似乎有些不对劲;你将viewviewTmp都赋值为getCurrentFocus(),所以它们始终会是相同的值。 - Andy Dennie

1
非常困难的决定。我建议采用更简单的解决方案。 在onViewCreated中,我们为整个视图设置了一个setOnClickListener,并从EditText中移除了焦点,同时也移除了键盘。

Fragment中的示例。Java。

@Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
    super.onViewCreated(view, savedInstanceState);

    nameTextInputEditText = view.findViewById(R.id.NameTextInputEditText);

    view.setOnClickListener(v -> {
        nameTextInputEditText.clearFocus();

        InputMethodManager imm = (InputMethodManager) requireActivity().getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
    });
}

1
好的,您可以使用这段代码。请使用您的主布局ID替换"mainRelativeLayout"。
//hide Soft keyboard on click outside  the input text
    findViewById(R.id.mainRelativeLayout).setOnClickListener(new 
View.OnClickListener() {
        @Override
        public void onClick(View v) {
            InputMethodManager im = (InputMethodManager) 
getSystemService(INPUT_METHOD_SERVICE);
            im.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 
0);
        }

    });

1

我对@navneeth-g的答案进行了一些修改,添加了空活动焦点处理,避免了多个OnTouchListener实例的创建,在键盘隐藏时移除焦点,并删除了屏幕滚动时隐藏键盘的操作,这对小设备非常方便。

//In general, the view parameter is root layout
fun Activity.hideKeyboardOnClickOutsideEditText(view: View) {
    // Set up touch listener for non-text box views to hide keyboard.
    var previousAction = 0
    val onTouchListener = View.OnTouchListener { v, event ->
        if (currentFocus != null
            && event.action != MotionEvent.ACTION_DOWN
            && event.action != MotionEvent.ACTION_MOVE
            && previousAction != MotionEvent.ACTION_MOVE
        ) {
            currentFocus?.clearFocus()
            v?.hideKeyboard()
        }
        previousAction = event.action
        false
    }

    if (view !is EditText) {
        view.setOnTouchListener(onTouchListener)
    }

    //If a layout container, iterate over children and seed recursion.
    if (view is ViewGroup) {
        for (i in 0 until view.childCount) {
            val innerView = view.getChildAt(i)
            hideKeyboardOnClickOutsideEditText(innerView)
        }
    }
}
   
//in root layout.xml
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"

谢谢!在尝试了许多方法后,它帮助了我。 - iamkdblue
不工作。当点击EditText之外时,它不会隐藏键盘。 - CoolMind

1
@Override
    public boolean onTouchEvent(MotionEvent event) {
        InputMethodManager imm = (InputMethodManager)getSystemService(Context.
                INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
        return true;
    }

这肯定会做到的。 - Jeffy
这个肯定不行。它可以关闭键盘,但不能像问题所提出的那样关闭EditText外的触摸。 - Gustavo Baiocchi Costa
1
为什么不呢?我采用了相同的方法。完美地运行着。在我的版本中,我将它放在dispatchTouchEvent()里。 - user1506104

1

在@sumit sonawane之前的回答上进行补充,该解决方案不会在用户滚动时隐藏键盘。

public long pressTime = 0;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        pressTime = System.currentTimeMillis();
    }
    else if (ev.getAction() == MotionEvent.ACTION_UP) {
        long releaseTime = System.currentTimeMillis();
        if (releaseTime-pressTime < 200) {
            if (getCurrentFocus() != null) {
                GhostTube.print("BottomNavActivity", "Touch event with keyboard detected...");
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
            }
        }
    }
    return super.dispatchTouchEvent(ev);
}

在您的活动中添加代码,也会处理片段。


在NestedScrollView中运行良好。滚动时,它不会隐藏键盘。 - CoolMind

0

我的解决方案可以在任何活动中通过外部点击隐藏键盘,而无需逐个指定所有编辑文本。

首先,在布局xml的根视图中添加: android:clickable="true" android:focusableInTouchMode="true"

接下来,创建一个父活动,包含您想要隐藏键盘的所有活动,并指定onResume()方法:

 @Override
    protected void onResume() {
        super.onResume();
        //getting Root View that gets focus
        View rootView =((ViewGroup)findViewById(android.R.id.content)).
                getChildAt(0);
        rootView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    hideKeyboard(AbstractActivity.this);
                }
            }
        });
    }

通过使用通用活动(继承功能!)扩展您的活动,这样每当任何EditText(在任何扩展的活动中)失去焦点时,键盘都会被隐藏。

P.S. hideKeyboard方法:

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

context.getCurrentFocus() 不需要指定具体的 EditText 视图。


0

你可以尝试以下方法,对我来说效果非常好 :)

这种方法适用于Activity或Fragment,并且也兼容ScrollView。

我们将ScrollView作为顶层布局,声明LinearLayout内部的id parentView,并添加以下两个属性:

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

在代码中,编写一个如下的函数:
public static void hideSoftKeyboard (Activity activity) {
        InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
        inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
    }

然后在 onCreate 方法中为根视图注册一个 OnFocusChangeListener,以使 Activity 中的所有 EditText 受到影响:

parentLayout.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    hideSoftKeyboard(your_activity_name.this);
                }
            }
        });

0

唯一有效的代码

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

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