EditText, clear focus on touch outside
I tried all these solutions. edc598's was the closest to working, but touch events did not trigger on other View
s contained in the layout. In case anyone needs this behavior, this is what I ended up doing:
I created an (invisible) FrameLayout
called touchInterceptor as the last View
in the layout so that it overlays everything (edit: you also have to use a RelativeLayout
as the parent layout and give the touchInterceptor fill_parent
attributes). Then I used it to intercept touches and determine if the touch was on top of the EditText
or not:
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;
}
});
Return false to let the touch handling fall through.
It's hacky, but it's the only thing that worked for me.
Just put these properties in the top most parent.
android:focusableInTouchMode="true"
android:clickable="true"
android:focusable="true"
For the EditText's parent view, let the following 3 attributes be "true":
clickable, focusable, focusableInTouchMode.
If a view want to receive focus, it must satisfy these 3 conditions.
See android.view :
public boolean onTouchEvent(MotionEvent event) {
...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
...
if (isFocusable() && isFocusableInTouchMode()
&& !isFocused()) {
focusTaken = requestFocus();
}
...
}
...
}
Hope it helps.
Building on Ken's answer, here's the most modular copy-and-paste solution.
No XML needed.
Put it in your Activity and it'll apply to all EditTexts including those within fragments within that activity.
@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 );
}