EditTextの外側をクリックした後、Androidでソフトキーボードを非表示にする方法は?


354

キーボードを非表示にするには、実装する必要があることを誰もが知っています。

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

しかし、ここで重要なのEditTextは、ユーザーが、またはsoftKeyboard ではない他の場所をタッチまたは選択したときにキーボードを非表示にする方法です。

onTouchEvent()親でon を使用しようとしましたが、これは、Activityユーザーが他のビューの外側をタッチし、スクロールビューがない場合にのみ機能します。

タッチ、クリック、フォーカスのリスナーを実装しようとしましたが、成功しませんでした。

タッチイベントをインターセプトするために独自のスクロールビューを実装することさえ試みましたが、クリックされたビューではなく、イベントの座標しか取得できません。

これを行う標準的な方法はありますか?iPhoneではそれは本当に簡単でした。


まあ、私はスクロールビューが本当に問題ではなく、そこにあるラベルであることに気づきました。ビューは、TextView、EditText、TextView、EditTextなどの垂直レイアウトで、textViewsはedittextがフォーカスを
失っ

あなたはのための解決策を見つけることができますgetFields():ここstackoverflow.com/questions/7790487/...
レト

キーボードはリターンボタンを押すことで閉じることができるので、これが努力の価値があるかどうかは疑問です
gerrytan 2013年

4
私はこの答えを見つけました:stackoverflow.com/a/28939113/2610855最高のもの。
Loenix 2015

回答:


575

次のスニペットは、キーボードを単に非表示にします。

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

これをユーティリティクラスに含めるか、アクティビティ内で定義する場合は、アクティビティパラメータを回避するか、を呼び出します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>

次に、クラスを拡張してその中Activityで定義setupUI(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 2013年

4
非常に難しいことではありませんか?現在、Androidプログラミングは終了しているので、間違っている場合は修正してください。何らかの方法でフォーカスされたEditTextを何らかの方法で追跡し、OnTouchEventの間にフォーカスを失うように要求することができますか?
Navneeth G 2013

25
他の誰かがこの問題に遭遇したかどうかはわかりませんが、何もフォーカスされていない場合にhideSoftKeyboardを呼び出すと、アプリがクラッシュします。あなたは持つメソッドの2行目を囲むことにより、この問題を解決することができますif(activity.getCurrentFocus() != null) {...}
フランクCangialosi

14
このアプローチの問題は、他のすべてのビューOnTouchListenerでそれらを設定する必要がないことを前提としていることです。そのロジックをViewGroup.onInterceptTouchEvent(MotionEvent)ルートビューに設定するだけです。
Alex.F 14

2
キーボードを開いたまま他のコントロールをクリックしても機能しない
mohitum

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

以下のコメントの1つで指摘されているように、親ビューがScrollViewの場合、これは機能しない可能性があります。このような場合、ClickableおよびfocusableInTouchModeをScrollViewの直下のビューに追加できます。


67
私の意見では、これは正しい答えです。コードが少なく、不必要な繰り返しがありません...
gattshjoty 14

14
私はこの答えがとても好きです。注意すべき点の1つは、ルート要素を追加するとき、clickableおよびfocusableInTouchModeルートScrollView要素に追加するときに、これが機能しなかったことです。私はの直接の親に追加する必要がEditTextありましたLinearLayout
アダムジョンズ

10
私には完璧に働きました。ただし、edittextウィジェットが2つある場合は、両方のonfocusを正しく処理する必要があります。そうしないと、キーボードの非表示が不必要に非表示になります。
Marka A

1
@MarkaA私はこれで問題ありませんでした。他のEditTextをクリックすると、キーボードが残ります。背景をクリックすると、本来のように非表示になります。onFocusChangeで他にいくつかの処理を行いました。フォーカスがあるときにEditTextの背景を変更するだけで、空想はありません。
CularBytes 2015

3
@Shrikant-複数の編集テキストでちらつきも発生していました。各編集テキストではなく親にonFocusChangeListenerを設定し、条件を切り替えて(hasFocus){hideKeyboard(v); } btwn編集テキストの切り替えのちらつきに気づかなかった。
マニシャ2018

69

アクティビティのコードの下をオーバーライドするだけです

 @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
もう1つの解決策は、BaseActivityを作成し、それをすべてのアクティビティで拡張することです
sumit sonawane

1
素晴らしいソリューション
アーメドアデルイスマイル

1
キーボードを閉じるときに画面がちらつくのを防いでくれました。いいぞ!
Dimas Mendes

1
いいですが、本当であるにはあまりにも良かったです。単純で、非常に短く、うまくいきました...悲しいかな、問題があります。キーボードが表示されているときに、キーボードに尋ねた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を取得しています。私は何かを逃していますか?
カッツェンハット2014

どこかでコードを参照できますか?レイアウトとアクティビティ/フラグメントなどのコードを確認できない場合、何が問題なのかわかりません。
roepit 14

それがどのように機能するかについて私の頭を包み込もうとしています。
user40797

アプリのタイトルバーをクリックすると、これは機能しますか?
user1506104

1
これはキーボードを非表示にするのに最適です!これはEditTextのフォーカスを実際に解除するのではなく、キーボードを非表示にするだけです。EditTextのフォーカスを解除するには、たとえばandroid:onClick="stealFocusFromEditTexts"親ビューのxmlに追加してpublic void stealFocusFromEditTexts(View view) {}から、そのアクティビティに追加します。on-clickメソッドは何もする必要はありません。親ビューがフォーカス可能/選択可能になるために存在する必要があります。これは、子EditTextからフォーカスを盗むために必要です
Jacob R

40

私はキーボードを隠すためのもう一つの解決策を得ました:

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

ここでは合格HIDE_IMPLICIT_ONLYの位置でshowFlag0の位置にhiddenFlag。ソフトキーボードを強制的に閉じます。


3
おかげでうまくいきました.....何よりも試してみましたが、ダイアログボックスeditextテキストnd closoingダイアログボックスから値を取得しているときに機能しません...
PankajAndroid

おかげで、これは機能しているだけで、上記のものよりもはるかにきれいです!+1
エドモンドタマス

期待どおりに機能しています。ソリューションをありがとう+1
tryp

私にとっては魅力のように機能します。エレガントなソリューションの場合は+1。
セントジャブ2016年

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を取得するメソッドを作成して終了しました。それ以外の場合、キーボードは他の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年

この場合、もう1つの解決策があります。答えを更新しました。
Sergey Glotov

2
onTouchEventは何度も呼び出されるため、これも良い方法ではありません
Jesus Dimrix

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 14年

getActionMasked(ev)は非推奨であるため、以下を使用してください。final int action = ev.getActionMasked(); 最初の行。
Andrew

12

TextInputEditTextを使用したよりKotlinMaterial 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> 

それで全部です。この繰り返しのケースを処理するためにコントローラー(フラグメントまたはアクティビティー)を変更する必要はありません


うん、いいね。でももっと簡単な方法があればいいのに!
devDeejay

11

任意のアクティビティで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;
}

そして、それはあなたがする必要があるすべてです


これは私がそれを機能させるために見つけた最も簡単な方法でした。複数のEditTextとScrollViewで動作します
RJH

複数のEditTextでテストしました。できます!唯一の欠点は、ドラッグ操作を行うと非表示になることです。
RominaV 2016年

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はデフォルトで消費するビューの1つです)、ルートビューに到達した場合、それは消費されなかったためです。ソフトキーボードを閉じます。

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関数を「フック」しました。主に2つの条件があります。

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

本当にありがとうございます!! 私はあなたの答えに1つの賛成票を入れました。
ビジェイ2015年

2
これが複雑なレイアウトの最良の解決策だと思います。しかし、これまでに2つの欠点が見つかりました。1. EditTextコンテキストメニューがクリックできない-クリックするとEditTextからフォーカスが失われます。2. EditTextがビューの下部にあり、長い間(単語を選択するため)クリックすると、 「クリックポイント」がEditTextではなくキーボード上にあることを示すキーボード-したがって、再びフォーカスを失います:/
sosite

@sosite、私は私の回答でこれらの制限に対処したと思います。見てください。
アンディデニー2015

6

私はdispatchTouchEventhtafoyaによる呼び出しのアプローチが好きでしたが、

  • タイマーの部分がわからなかった(ダウンタイムの測定が必要な理由がわからない)。
  • すべての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());
}

欠点が1つあります。

キーボードEditTextを切り替えるEditTextと、キーボードが非表示になり、再表示されます。私の場合は、2つの入力コンポーネントを切り替えたことを示しているため、この方法が望まれます。


このメソッドは、私のFragmentActivityでプラグアンドプレイできるという点で最も効果的でした。
BillyRayCyrus

ありがとう、それが最善の方法です!イベントアクションのチェックも追加しました:int action = ev.getActionMasked(); if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN){...}
sergey.n

よろしくお願いします!あなたは私の時間を節約しました。
I.d007 2018

6

プレア:私は影響力がないと認識していますが、私の答えを真剣に受け止めてください。

問題:キーボードから離れたところからソフトキーボードを閉じるか、最小限のコードでテキストを編集します。

解決策:バターナイフと呼ばれる外部ライブラリ

1行のソリューション:

@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の場合、バターナイフバインド呼び出しは次のようになります。

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

バターナイフのドキュメントリンク: http ://jakewharton.github.io/butterknife/

プラグイン:バターナイフは、Android開発に革命をもたらします。考慮して下さい。

注:外部ライブラリーバターナイフを使用しなくても、同じ結果が得られます。上記のように、OnClickListenerを親レイアウトに設定するだけです。


素晴らしい、完璧なソリューション!ありがとうございました。
nullforlife 2017年

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

sositeによって提起された問題に対処するfjeの回答の別のバリエーションを次に示します。

ここでの考え方は、アクティビティのdispatchTouchEventメソッドでダウンアクションとアップアクションの両方を処理することです。ダウンアクションでは、現在フォーカスされているビュー(存在する場合)と、その内部にタッチがあったかどうかを記録し、後で両方の情報を保存します。

upアクションでは、最初にディスパッチして、別のビューが潜在的にフォーカスを取得できるようにします。その後、現在フォーカスされているビューが最初にフォーカスされているビューであり、ダウンタッチがそのビュー内にあった場合は、キーボードを開いたままにします。

現在フォーカスされているビューが最初にフォーカスされているビューは異なりそれがEditText場合、キーボードも開いたままにします。

それ以外の場合は閉じます。

つまり、要約すると、これは次のように機能します。

  • 現在フォーカスされている内部に触れたとき EditTextと、キーボードは開いたままになります
  • 集中しているものEditTextから別のものに移動するときEditText、キーボードは開いたままです(閉じたり開いたりしません)
  • 現在フォーカスされEditTextている別の場所以外のどこかに触れたときEditTextと、キーボードが閉じます
  • EditTextコンテキストアクションバー(切り取り/コピー/貼り付けボタン付き)を表示するために長押しすると、フォーカスされたEditText(CABのためのスペースを空けるために下に移動した)の外側でUPアクションが実行されても、キーボードは開いたままになります。。ただし、CABのボタンをタップすると、キーボードが閉じることに注意してください。それは望ましい場合と望ましくない場合があります。あるフィールドから切り取り/コピーして別のフィールドに貼り付けたい場合は、そうなります。同じに貼り付けたい場合はEditText、そうではありません。
  • フォーカスEditTextが画面の下部にあるときにテキストを長押しして選択すると、EditTextフォーカスが維持されるため、キーボードが望みどおりに開きます。 、アップアクションではありません。

    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 2019

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 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コードの問題点を説明することをお勧めします。
ピロー2017

はい@pirho私もあなたに同意しますハシーブは適切な答えを与えることに集中する必要があります。
Dilip

2
@Dilip同意するコメントにも賛成投票できることをご存知ですか?これは、コメントのセクションを簡潔に保つためであり、実際には多くのコメントが同じ点を持たないようにします。
pirho

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

私はフェルナンドカマラゴのソリューションのわずかなバリエーションでこれを機能させました。私のonCreateメソッドでは、ルートビューに1つの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);
}

ここでのベストプラクティスは、ヘルパークラスを作成することであり、すべてのコンテナの相対/線形レイアウトはこれを実装する必要があります。

****メインコンテナのみがこのクラスを実装する必要があることに注意してください(最適化のため)****

次のように実装します。

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

何かがここでは正しく見えません。あなたは両方を割り当てているviewviewTmpgetCurrentFocus()、彼らは常に同じ値であることを行っているので、。
アンディデニー

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

できた

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.