目的

EditText以外の部分をタップしてキーボードを閉じる処理のメモ
個人的に結構厄介な設定だと思いました。

親のレイアウトによって、設定が変わりますし、Fragment上でもさらに設定方法が変わるようです。

バージョン

  • Android Studio 3.5
  • Java
  • Kotlin

Javaでの実装方法

まずxmlを説明します。
EditTextの他に、ダミーとなるTextView を用意します。
そこに
android:focusable="true"android:focusableInTouchMode="true" を設定することが必要だと思いました。

ダミー ってなんぞ!?というと。
EditText以外の部分をタップしたときの代わりにターゲットとなるオブジェクト のことを言っています。
伝わりづらい方はおまじないと思っても。

EditTextのxml
<TextView
    android:id="@+id/view"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:focusable="true"
    android:focusableInTouchMode="true"
    tools:ignore="MissingConstraints" />
<EditText
    android:id="@+id/edittext"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="100dp"
    app:layout_constraintTop_toTopOf="parent" />

次にJavaコードの説明です。
Activityの場合以下のようになります。

画面外タップをonTouchEventで判定し、他オブジェクト(上で言ってたダミー )にフォーカスを当てます。
キーボード非表示のhideSoftInputFromWindowも必要です。

Activityでの処理
public class MainActivity extends AppCompatActivity {

    private TextView view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        view = findViewById(R.id.idtext);

        EditText edittext = findViewById(R.id.registinheritEdit1);
        edittext.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if(!hasFocus) {
                    //キーボード非表示
                    InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
                    if (imm != null) {
                        imm.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
                    }
                }
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        view.requestFocus();
        return super.onTouchEvent(event);
    }
}

ただ、
親LayoutがdrawerlayoutListView とかですとonTouchEventが呼ばれないようです。

その時はdispatchTouchEvent を利用します。

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    view.requestFocus();
    return super.dispatchTouchEvent(ev);
}

Fragmentの場合

Activityの場合は上記でよいですが、
Fragmentですと、これまた別の処理が必要です。

この辺がAndroid野ややこしい所。。

FragmentでもTouchEventが呼ばれないようなので、
onCreateView で、以下の設定をするべきと思いました。

view.setOnTouchListener(new View.OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_MOVE){
        }
        return true;
    }
});

挙動

挙動については以下のようになります。

kotlinでの実装

次にkotlin言語での説明です。
xmlはJavaの時と変わらずです。

ただ、kotlinの場合はFragmentだけでのメモです。

Fragmentでの処理
edittext.setOnFocusChangeListener { v, hasFocus ->
    if (!hasFocus) {
        //キーボード非表示
        val imm = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(v.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
    }
}

view.setOnTouchListener { v, event ->
    view.requestFocus()
    v?.onTouchEvent(event) ?: true
}

メモは以上です。

スッキリわかるJava入門 第2版 (スッキリシリーズ)