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


354

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

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

但是最重​​要的是,当用户触摸或选择其他非键盘位置时,如何隐藏键盘。 EditText或softKeyboard的?

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

我尝试实现触摸,单击,焦点侦听器,但没有成功。

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

有标准的方法吗?在iPhone中,这真的很容易。


好吧,我意识到滚动视图并不是真正的问题,而是其中存在的标签。该视图是垂直布局,具有以下内容:TextView,EditText,TextView,EditText等。并且textViews不会让edittext失去焦点并隐藏键盘
htafoya 2010年

您可以在getFields()这里找到解决方案:stackoverflow.com/questions/7790487/…–
Reto

可以通过按回车键关闭键盘,所以我要说这是否值得付出努力是值得怀疑的
gerrytan

4
我找到了这个答案:stackoverflow.com/a/28939113/2610855最好的一个。
Loenix 2015年

Answers:


575

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

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

您可以将其放在实用程序类中,或者如果要在活动中定义它,请避免使用activity参数或调用 hideSoftKeyboard(this)

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

希望能帮助到你。

如果您使用多个活动,请为父级布局定义通用ID,例如 <RelativeLayout android:id="@+id/main_parent"> ... </RelativeLayout>

然后从中扩展一个类ActivitysetupUI(findViewById(R.id.main_parent))在其中定义OnResume()并扩展该类,而不是``活动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)
    }
}

我尚未进行自我测试,但是看起来它可以工作,并且具有很高的评价,因此我将更改对此可接受的答案。
htafoya

4
不应该很辛苦吗?我现在没有使用Android编程,所以如果我错了,请纠正我。您可以随时以某种方式跟踪聚焦的EditText,而只是请求它在OnTouchEvent期间失去焦点?
Navneeth G

25
不知道是否有人遇到过此问题,但是如果没有重点关注,这会导致在调用hideSoftKeyboard时应用程序崩溃。您可以通过以下方法解决此问题:if(activity.getCurrentFocus() != null) {...}
Frank Cangialosi

14
这种方法的问题在于,它假定所有其他视图都不需要为其设置OnTouchListener。您可以只在ViewGroup.onInterceptTouchEvent(MotionEvent)根视图中设置该逻辑。
Alex.F 2014年

2
当我单击其他控件并保持键盘打开时
无法

285

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

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

        android:clickable="true" 
        android:focusableInTouchMode="true" 
  2. 实现hideKeyboard()方法

        public void hideKeyboard(View view) {
            InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
  3. 最后,设置您的edittext的onFocusChangeListener。

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

正如下面的注释之一所指出的,如果父视图是ScrollView,则此方法可能不起作用。对于这种情况,可以将clickable和focusableInTouchMode添加到直接在ScrollView下的视图上。


67
我认为这是正确的答案。更少的代码,没有不必要的迭代...
gattshjoty 2014年

14
我很喜欢这个答案。有一点要注意的是,这并没有为我加时工作clickablefocusableInTouchMode我的根ScrollView元素。我不得不添加到我的直接父EditText这是一个LinearLayout
亚当·约翰斯

10
为我完美地工作。但是,如果您有两个edittext小部件,则需要确保正确处理它们的onfocus,否则将不必要地切换键盘隐藏。
Marka A

1
@MarkaA我对此没有任何问题,当我单击另一个EditText时,键盘保持不变,如果单击背景,它会像应该的那样隐藏。我在onFocusChange上确实做了一些其他处理,只是在EditText具有焦点时更改了背景,没有什么花哨的地方。
CularBytes

3
@Shrikant-我也看到带有多个编辑文本的闪烁。我只是将onFocusChangeListener设置在父项上,而不是在每个编辑文本上,并切换条件以说是否(hasFocus){hideKeyboard(v); }尚未注意到闪烁会切换btwn编辑文本。
manisha

69

只需覆盖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);
}

4
简单的解决方案,添加活动,它也会处理碎片
Rohit Maurya

1
另一个解决方案是创建BaseActivity并将其扩展到所有活动中
sumit sonawane

1
出色的解决方案
艾哈迈德·阿德尔·伊斯梅尔

1
您可以避免关闭键盘时屏幕闪烁。竖起大拇指!
Dimas Mendes

1
不错,但是太好了,难以置信:简单,非常简短,而且可以正常工作……a,这是一个问题:显示键盘时,每次我们触摸询问键盘的EditText时,它都会掉下来,自动上升。
Chrysotribax

60

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

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

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

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

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

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


@roepit-我由于尝试将布局投射到视图而收到classCastexception。我想念什么吗?
katzenhut 2014年

您可以在某处引用您的代码吗?当我看不到您的布局和活动/片段等代码时,我不知道出了什么问题。
roepit 2014年

最好的答案在那里,仍然试图围绕它的工作原理。
user40797

如果您单击应用程序的标题栏,此方法会起作用吗?
user1506104

1
这非常适合隐藏键盘!请注意,这实际上并没有使EditText失去焦点,只是隐藏了键盘。要使EditText失去焦点android:onClick="stealFocusFromEditTexts",请在父视图的xml中添加例如,然后public void stealFocusFromEditTexts(View view) {}在其活动中添加。单击方法不需要做任何事情,只需要存在父视图就可以聚焦/选择的方法,这对于窃取子EditText的焦点是必要的
Jacob R

40

我还有另一种隐藏键盘的方法:

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

路过这里HIDE_IMPLICIT_ONLY时的位置showFlag,并0在该位置hiddenFlag。它将强行关闭软键盘。


3
谢谢,它的工作.....最重要的是我尝试过,但是当我从对话框中获取价值时,editext text和closoing对话框...
PankajAndroid

谢谢,这仅在工作,并且比上面的其他方法干净得多!+1
Edmond Tamas 2014年

它正在按预期工作,谢谢您的解决方案+1
tryp

对我来说就像一个魅力。+1为优雅的解决方案。
saintjab '16

2
抱歉,但是此方法已切换,因此如果键盘状态已经关闭,它将显示键盘
HendraWD

16

好吧,我设法某种程度上解决了这个问题,我在我的活动中覆盖了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};


这不是完美的,拖动事件的时间仅基于启发式方法,因此有时在执行较长的操作时它不会隐藏。否则,当单击其他EditText时,键盘将隐藏并显示。

不过,欢迎使用更清洁,更短的解决方案


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

14

使用OnFocusChangeListener

例如:

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

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


9
问题是,当我单击“标签”或其他无法聚焦的视图时,edittext不会失去焦点。
htafoya 2010年

在这种情况下,我还有一个解决方案。我已经更新了答案。
谢尔盖·格洛托夫

2
onTouchEvent调用了很多次,所以这也不是一个好习惯
Jesus Dimrix '16

13

我在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,那么我们也需要考虑这一点,但是我喜欢您的回答:-)
Lalit Poptani 2014年

不赞成使用getActionMasked(ev),因此现在使用:final int action = ev.getActionMasked(); 第一行。
Andrew

12

使用TextInputEditText的另一种Kotlin材料设计方法(该方法也与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> 

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


是的,但是我希望有一个更简单的方法!
devDeejay

11

覆盖任何Activity中的公共布尔型dispatchTouchEvent(MotionEvent event)(或扩展Activity的类)

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

这就是您需要做的


这是我发现使其正常工作的最简单方法。与多个EditTexts和ScrollView一起使用
RJH

用多个EditTexts进行了测试;有用!唯一的缺点是,当您进行拖动运动时,它也会隐藏。
RominaV

9

我修改了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;
    }
});

1
这对我来说似乎更安全。
superarts.org 2014年

9

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

我的所有活动都需要这种行为,因此我创建了一个从类Activity继承的CustomActivity类,并“钩住了” dispatchTouchEvent函数。主要要满足两个条件:

  1. 如果焦点保持不变,并且有人在当前输入字段之外点击,则关闭IME
  2. 如果焦点已更改并且下一个焦点元素不是任何类型的输入字段的实例,则关闭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);
}

超级它的工作很好,谢谢!我已对你的回答投了赞成票。
2015年

2
我认为这是复杂布局的最佳解决方案。但是到目前为止,我发现了2个缺点:1. EditText上下文菜单不可单击-单击该菜单会导致EditText失去焦点。2.当我们的EditText位于视图底部并且我们长按它(以选择单词)时,然后键盘显示我们的“点击点”在键盘上,而不是在EditText上,因此我们再次失去了焦点:/
sosite

@sosite,我想我已经在回答中解决了这些限制;看一看。
安迪·丹妮

6

我喜欢dispatchTouchEventhtafoya 进行呼叫的方法,但是:

  • 我不了解计时器部分(不知道为什么要测量停机时间?)
  • 我不喜欢在每次视图更改时都注册/注销所有EditText(在复杂的层次结构中可能有很多viewchange和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即插即用而言,这种方法效果最好。
BillyRayCyrus

谢谢,这是最好的方法!我还添加了对事件操作的检查:int action = ev.getActionMasked(); if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN){...}
sergey.n 2015年

谢谢 !您节省了我的时间。
I.d007 '18

6

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

问题:单击远离键盘或使用最少的代码编辑文本时,关闭软键盘。

解决方案:外部库称为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 Listener绑定到活动的XML Layout父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/

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

注意:无需使用外部库Butterknife即可获得相同的结果。只需如上所述将OnClickListener设置为父布局即可。


很棒,完美的解决方案!谢谢。
nullforlife

6

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

不能从片段开始工作
Nurseyit Tursunkulov

这可行,但是有一个错误。例如,如果我想在textview中粘贴文本,则隐藏键盘,然后显示出来。这有点烦人。
George Shalvashvili

那么告诉我,最好的解决办法..

4

这是fje回答的另一种变体,解决了sosite提出的问题。

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

在up操作中,我们首先调度,以使另一个视图有可能成为焦点。如果之后,当前聚焦的视图是原始聚焦的视图,而向下触摸位于该视图内部,则使键盘保持打开状态。

如果当前聚焦的视图与原始聚焦的视图不同并且是EditText,则我们也将键盘保持打开状态。

否则,我们将其关闭。

综上所述,其工作原理如下:

  • 触摸当前聚焦的内部时EditText,键盘保持打开状态
  • 当从一个专注的EditText转移到另一个EditText,键盘保持打开状态(不关闭/重新打开)
  • 当触摸当前焦点以外的EditText其他地方时EditText,键盘关闭
  • 长按以调出EditText上下文操作栏(带有“剪切/复制/粘贴”按钮)时,键盘会保持打开状态,即使UP操作是在焦点区域之外进行的EditText(向下移动以为CAB 留出空间) 。但是请注意,当您点击CAB中的按钮时,它将关闭键盘。这可能是合乎要求的,也可能是不合要求的。如果您想从一个字段剪切/复制并粘贴到另一个字段,那就可以了。如果要粘贴回相同的内容EditText,则不会。
  • 当焦点EditText位于屏幕底部并且您长时间单击某些文本以将其选中时,将EditText保持焦点,因此键盘将按您想要的方式打开,因为我们在向下操作中执行了“触摸位于视图范围内”的检查,而不是up操作。

    private View focusedViewOnActionDown;
    private boolean touchWasInsideFocusedView;
    
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                focusedViewOnActionDown = getCurrentFocus();
                if (focusedViewOnActionDown != null) {
                    final Rect rect = new Rect();
                    final int[] coordinates = new int[2];
    
                    focusedViewOnActionDown.getLocationOnScreen(coordinates);
    
                    rect.set(coordinates[0], coordinates[1],
                            coordinates[0] + focusedViewOnActionDown.getWidth(),
                            coordinates[1] + focusedViewOnActionDown.getHeight());
    
                    final int x = (int) ev.getX();
                    final int y = (int) ev.getY();
    
                    touchWasInsideFocusedView = rect.contains(x, y);
                }
                break;
    
            case MotionEvent.ACTION_UP:
    
                if (focusedViewOnActionDown != null) {
                    // dispatch to allow new view to (potentially) take focus
                    final boolean consumed = super.dispatchTouchEvent(ev);
    
                    final View currentFocus = getCurrentFocus();
    
                    // if the focus is still on the original view and the touch was inside that view,
                    // leave the keyboard open.  Otherwise, if the focus is now on another view and that view
                    // is an EditText, also leave the keyboard open.
                    if (currentFocus.equals(focusedViewOnActionDown)) {
                        if (touchWasInsideFocusedView) {
                            return consumed;
                        }
                    } else if (currentFocus instanceof EditText) {
                        return consumed;
                    }
    
                    // the touch was outside the originally focused view and not inside another EditText,
                    // so close the keyboard
                    InputMethodManager inputMethodManager =
                            (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                    inputMethodManager.hideSoftInputFromWindow(
                        focusedViewOnActionDown.getWindowToken(), 0);
                    focusedViewOnActionDown.clearFocus();
    
                    return consumed;
                }
                break;
        }
    
        return super.dispatchTouchEvent(ev);
    }

4

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

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

4

对于这个简单的要求,我发现接受的答案有点复杂。这是对我无故障的工作。

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

1
如果您使用NestedScrollView复杂的布局,请参见公认的答案:stackoverflow.com/a/11656129/2914140。您应该知道其他容器可能会消耗触摸。
CoolMind

3

有一个更简单的方法,基于iPhone同样的问题。只需在包含编辑文本的touch事件上覆盖背景的布局即可。只需在活动的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 2012年

1
如果背景布局包含其他子布局,则此方法效果不佳。
AxeEffect 2013年

感谢您提醒onClick不是唯一的选择。
Harpreet

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

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

这是隐藏键盘的方法。

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

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

android:onClick="hideKeyboard"

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

活动

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

ScreenUtils

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

3
这段代码很简单,但是有一个明显的问题:当触摸任何地方时,它都会关闭键盘。也就是说,如果您点击EditText的其他位置来移动输入光标,它将隐藏键盘,并且系统会再次弹出键盘。
该死的蔬菜

2

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

3
尽管这可能会回答问题,但最好解释一下答案的基本部分以及OPs代码可能存在的问题。
pirho

是的,@ pirho我也同意你的看法。Haseeb需要集中精力给出正确的答案。
Dilip

2
@Dilip您知道您也可以赞成您的评论吗?这只是为了保持注释部分的整洁,因此不要有太多的注释实际上具有相同的观点。
pirho's

2

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

为什么不仅仅覆盖Activity的onUserInteraction(),这可以确保每当用户在EditText之外点击时,键盘都会关闭。

即使EditText位于scrollView内也可以使用。

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

这是一个更好的答案
ovluca

是! 这是一个超级干净的答案。而且,如果您创建了一个其他所有活动都可以扩展的AbstractActivity,则可以将其作为整个应用程序中的默认行为来进行烘焙。
wildcat12

1

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

1

这可能很旧,但是我通过强制自定义课程来使它起作用

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

此处的最佳实践是创建一个Helper类,并且每个容器“相对/线性布局”都应实现此功能。

****请注意,只有主容器才能实现此类(用于优化)****

并像这样实现它:

Parent.setOnClickListener( new DismissKeyboardListener(this) ); 

用于活动的关键字。因此,如果您使用的是片段,则使用类似getActivity();的方法。

---如果有帮助,请竖起大拇指...--为拉尔夫加油---


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

这里看起来不对劲;您同时将view和分配viewTmpgetCurrentFocus(),因此它们将始终是相同的值。
安迪·丹妮

1

我这样做:

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

完成了

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.