レイアウトには、ListView
、SurfaceView
、およびEditText
が含まれています。 EditText
をクリックすると、フォーカスが表示され、画面上のキーボードがポップアップします。 EditText
の外側のどこかをクリックしても、フォーカスがあります(そうすべきではありません)。レイアウトの他のビューにOnTouchListener
を設定し、EditText
のフォーカスを手動でクリアできると思います。しかし、あまりにもハックが多いようです...
他のレイアウトにも同じ状況があります-さまざまな種類のアイテムが含まれるリストビューで、一部にはEditText
が含まれています。上で書いたように動作します。
タスクは、ユーザーが外部の何かに触れたときにEditText
フォーカスを失うようにすることです。
私はここで同様の質問を見ましたが、解決策は見つかりませんでした...
これらすべてのソリューションを試しました。 edc598は作業に最も近いものでしたが、タッチイベントはレイアウトに含まれる他のView
sでトリガーしませんでした。誰かがこの動作を必要とする場合、これは私がやったことです:
touchInterceptorと呼ばれる(見えない)FrameLayout
をレイアウトの最後のView
として作成し、すべてをオーバーレイするようにしました(- 編集:また、親レイアウトとしてRelativeLayout
を使用し、touchInterceptorfill_parent
属性を指定する必要があります)。次に、それを使用してタッチをインターセプトし、タッチがEditText
の上にあるかどうかを判断します。
FrameLayout touchInterceptor = (FrameLayout)findViewById(R.id.touchInterceptor);
touchInterceptor.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mEditText.isFocused()) {
Rect outRect = new Rect();
mEditText.getGlobalVisibleRect(outRect);
if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
mEditText.clearFocus();
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
}
return false;
}
});
Falseを返すと、タッチ処理が失敗します。
それはハックですが、それは私のために働いた唯一のものです。
Kenの答えに基づいて、最もモジュール化されたコピーアンドペーストソリューションを紹介します。
XMLは必要ありません。
それをアクティビティに入れると、そのアクティビティ内のフラグメント内のものを含むすべてのEditTextに適用されます。
@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())) {
v.clearFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
}
return super.dispatchTouchEvent( event );
}
EditTextの親ビューの場合、次の3つの属性を「true」にします。
クリック可能、フォーカス可能、focusableInTouchMode。
ビューがフォーカスを取得する場合は、これらの3つの条件を満たしている必要があります。
Android.viewを参照してください:
public boolean onTouchEvent(MotionEvent event) {
...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
...
if (isFocusable() && isFocusableInTouchMode()
&& !isFocused()) {
focusTaken = requestFocus();
}
...
}
...
}
それが役に立てば幸い。
これらのプロパティを最上位の親に配置するだけです。
Android:focusableInTouchMode="true"
Android:clickable="true"
Android:focusable="true"
ケンの答えはうまくいきますが、それはハッキーです。 pcansが回答のコメントで暗示しているように、dispatchTouchEventでも同じことができます。このソリューションは、透明なダミーFrameLayoutを使用してXMLをハッキングする必要がないため、よりクリーンです。これは次のようなものです。
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
EditText mEditText = findViewById(R.id.mEditText);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if (mEditText.isFocused()) {
Rect outRect = new Rect();
mEditText.getGlobalVisibleRect(outRect);
if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
mEditText.clearFocus();
//
// Hide keyboard
//
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
}
return super.dispatchTouchEvent(event);
}
おそらくこの問題に対する答えはすでに見つかっていますが、これを解決する方法を探していましたが、探しているものを正確に見つけることができなかったので、ここに投稿すると思いました。
私がやったことは次のことでした(これは非常に一般化されており、目的はすべてのコードをコピーして貼り付ける方法がO:Dで機能しないというアイデアをあなたに与えることです):
まず、プログラム内で必要なEditTextおよびその他のビューを単一のビューでラップします。私の場合、LinearLayoutを使用してすべてをラップしました。
<LinearLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/mainLinearLayout">
<EditText
Android:id="@+id/editText"/>
<ImageView
Android:id="@+id/imageView"/>
<TextView
Android:id="@+id/textView"/>
</LinearLayout>
次に、コードでTouch ListenerをメインのLinearLayoutに設定する必要があります。
final EditText searchEditText = (EditText) findViewById(R.id.editText);
mainLinearLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if(searchEditText.isFocused()){
if(event.getY() >= 72){
//Will only enter this if the EditText already has focus
//And if a touch event happens outside of the EditText
//Which in my case is at the top of my layout
//and 72 pixels long
searchEditText.clearFocus();
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
Toast.makeText(getBaseContext(), "Clicked", Toast.LENGTH_SHORT).show();
return false;
}
});
これが一部の人々に役立つことを願っています。または、少なくとも問題の解決を開始するのに役立ちます。
私の下で働いていたもの-
1 .parentLayout
i.e Android:clickable="true"
のEditText
にAndroid:focusableInTouchMode="true"
およびAndroid.support.design.widget.TextInputLayout
を追加
<Android.support.design.widget.TextInputLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:clickable="true"
Android:focusableInTouchMode="true">
<EditText
Android:id="@+id/employeeID"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:ems="10"
Android:inputType="number"
Android:hint="Employee ID"
tools:layout_editor_absoluteX="-62dp"
tools:layout_editor_absoluteY="16dp"
Android:layout_marginTop="42dp"
Android:layout_alignParentTop="true"
Android:layout_alignParentRight="true"
Android:layout_alignParentEnd="true"
Android:layout_marginRight="36dp"
Android:layout_marginEnd="36dp" />
</Android.support.design.widget.TextInputLayout>
2 .アクティビティクラスでdispatchTouchEvent
をオーバーライドし、hideKeyboard()
関数を挿入
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
View view = getCurrentFocus();
if (view != null && view instanceof EditText) {
Rect r = new Rect();
view.getGlobalVisibleRect(r);
int rawX = (int)ev.getRawX();
int rawY = (int)ev.getRawY();
if (!r.contains(rawX, rawY)) {
view.clearFocus();
}
}
}
return super.dispatchTouchEvent(ev);
}
public void hideKeyboard(View view) {
InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
3 .EditTextにsetOnFocusChangeListener
を追加
EmployeeId.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard(v);
}
}
});
そのEditText
の親の2つのプロパティを次のように定義するだけです。
Android:clickable="true"
Android:focusableInTouchMode="true"
したがって、ユーザーがEditText
エリアの外側をタッチすると、フォーカスが親ビューに転送されるため、フォーカスが削除されます。
getLocationOnScreen
よりもgetGlobalVisibleRect
を使用する方がより堅牢な方法だと思います。問題に遭遇したからです。 Edittextを含むListviewがあり、アクティビティにajustpan
を設定します。 getGlobalVisibleRect
はscrollYを含むように見える値を返しますが、event.getRawYは常に画面のそばにあります。以下のコードはうまく機能します。
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if ( v instanceof EditText) {
if (!isPointInsideView(event.getRawX(), event.getRawY(), v)) {
Log.i(TAG, "!isPointInsideView");
Log.i(TAG, "dispatchTouchEvent clearFocus");
v.clearFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
}
return super.dispatchTouchEvent( event );
}
/**
* Determines if given points are inside view
* @param x - x coordinate of point
* @param y - y coordinate of point
* @param view - view object to compare
* @return true if the points are within view bounds, false otherwise
*/
private boolean isPointInsideView(float x, float y, View view) {
int location[] = new int[2];
view.getLocationOnScreen(location);
int viewX = location[0];
int viewY = location[1];
Log.i(TAG, "location x: " + location[0] + ", y: " + location[1]);
Log.i(TAG, "location xWidth: " + (viewX + view.getWidth()) + ", yHeight: " + (viewY + view.getHeight()));
// point is inside view bounds
return ((x > viewX && x < (viewX + view.getWidth())) &&
(y > viewY && y < (viewY + view.getHeight())));
}
ListView
ビューで構成されるEditText
があります。このシナリオでは、1つ以上の行のテキストを編集した後、「終了」というボタンをクリックする必要があるとしています。 onFocusChanged
内のEditText
ビューでlistView
を使用しましたが、[完了]をクリックした後、データが保存されていません。問題を追加することで解決しました
listView.clearFocus();
「完了」ボタンのonClickListener
内にあり、データが正常に保存されました。
他のビューがタッチされたときにフォーカスを失うには、両方のビューをview.focusableInTouchMode(true)として設定する必要があります。
ただし、タッチモードでのフォーカスの使用は推奨されないようです。こちらをご覧ください: http://Android-developers.blogspot.com/2008/12/touch-mode.html
@pcansが示唆したように、アクティビティでdispatchTouchEvent(MotionEvent event)
をオーバーライドできます。
ここで、タッチ座標を取得し、ビュー境界と比較します。ビューの外側でタッチが実行される場合は、何かをしてください。
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
View yourView = (View) findViewById(R.id.view_id);
if (yourView != null && yourView.getVisibility() == View.VISIBLE) {
// touch coordinates
int touchX = (int) event.getX();
int touchY = (int) event.getY();
// get your view coordinates
final int[] viewLocation = new int[2];
yourView.getLocationOnScreen(viewLocation);
// The left coordinate of the view
int viewX1 = viewLocation[0];
// The right coordinate of the view
int viewX2 = viewLocation[0] + yourView.getWidth();
// The top coordinate of the view
int viewY1 = viewLocation[1];
// The bottom coordinate of the view
int viewY2 = viewLocation[1] + yourView.getHeight();
if (!((touchX >= viewX1 && touchX <= viewX2) && (touchY >= viewY1 && touchY <= viewY2))) {
Do what you want...
// If you don't want allow touch outside (for example, only hide keyboard or dismiss popup)
return false;
}
}
}
return super.dispatchTouchEvent(event);
}
また、実行中にアクティビティのレイアウトが変更されない場合は、ビューの存在と可視性を確認する必要はありません(たとえば、フラグメントを追加したり、レイアウトからビューを置換/削除したりしません)。ただし、カスタムコンテキストメニュー(アイテムのオーバーフローメニューを使用する場合のGoogle Playストアなど)を閉じる(または同様の操作を行う)場合は、ビューの存在を確認する必要があります。それ以外の場合は、NullPointerException
を取得します。
最良の方法は、デフォルトのメソッド clearFocus()
を使用することです
onTouchListener
のコードを解決する方法を知っていますか?
EditText.clearFocus()
を呼び出すだけです。 last EditText
のフォーカスがクリアされます。
このシンプルなコードスニペットは、あなたが望むことをします
GestureDetector gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
KeyboardUtil.hideKeyboard(getActivity());
return true;
}
});
mScrollView.setOnTouchListener((v, e) -> gestureDetector.onTouchEvent(e));