EditText, очистить фокус при касании снаружи
Мой макет содержит ListView
, SurfaceView
и EditText
. Когда я нажимаю на EditText
, он получает фокус, и появляется экранная клавиатура. Когда я нажимаю где-то за пределами EditText
, он все еще имеет фокус (он не должен).
Думаю, я мог бы настроить OnTouchListener
на другие представления в макете и вручную очистить фокус EditText
. Но кажется слишком хакерским...
У меня также такая же ситуация в другом представлении макета - списка с разными типами элементов, некоторые из которых имеют EditText
внутри. Они действуют так же, как я писал выше.
Задача состоит в том, чтобы EditText
потерять фокус, когда пользователь прикасается к чему-то вне его.
Я видел похожие вопросы здесь, но не нашел никакого решения...
Ответы
Ответ 1
Я пробовал все эти решения. edc598 был самым близким к работе, но события касания не срабатывали на другом View
, содержащемся в макете. Если кому-то нужно это поведение, это то, что я закончил:
Я создал (невидимый) FrameLayout
, называемый touchInterceptor, как последний View
в макете, чтобы он накладывал все (edit:), а также использовать RelativeLayout
в качестве родительского макет и атрибуты touchInterceptor fill_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, чтобы обработать сенсорную обработку.
Это взломало, но это единственное, что сработало для меня.
Ответ 2
Создание ответа от Ken, здесь самое модульное решение для копирования и вставки.
Не требуется XML.
Поместите его в свою активность, и он применим ко всем файлам EditTexts, в том числе к фрагментам внутри этого действия.
@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 );
}
Ответ 3
Для родительского представления EditText допустим следующие 3 атрибута: true:
clickable, фокусируемая, focusableInTouchMode.
Если представление хочет получить фокус, оно должно удовлетворять этим 3 условиям.
См. android.view:
public boolean onTouchEvent(MotionEvent event) {
...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
...
if (isFocusable() && isFocusableInTouchMode()
&& !isFocused()) {
focusTaken = requestFocus();
}
...
}
...
}
Надеюсь, это поможет.
Ответ 4
Кен отвечает, но он взломан. Как отмечают pcans в ответе на ответ, то же самое можно сделать с dispatchTouchEvent. Это решение является более чистым, поскольку оно позволяет избежать взлома XML с помощью прозрачного, фиктивного FrameLayout. Вот что это выглядит:
@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);
}
Ответ 5
Вероятно, вы уже нашли ответ на эту проблему, но я искал, как это решить, и все еще не могу найти то, что я искал, поэтому решил, что отправлю его здесь.
Я сделал следующее (это очень обобщенно, цель - дать вам представление о том, как действовать, копировать и вставлять весь код не будет работать 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;
}
});
Надеюсь, это поможет некоторым людям. Или, по крайней мере, помогает им начать решение своей проблемы.
Ответ 6
Я действительно считаю это более надежным способом использования getLocationOnScreen
, чем getGlobalVisibleRect
. Потому что я встречаюсь с проблемой. Существует Listview, в котором содержатся некоторые Edittext и set 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())));
}
Ответ 7
Просто поместите эти свойства в самый верхний родительский.
android:focusableInTouchMode="true"
android:clickable="true"
android:focusable="true"
Ответ 8
Чтобы потерять фокус при касании другого вида, оба представления должны быть установлены как view.focusableInTouchMode(true).
Но кажется, что использование фокусов в сенсорном режиме не рекомендуется.
Пожалуйста, смотрите здесь:
http://android-developers.blogspot.com/2008/12/touch-mode.html
Ответ 9
У меня есть ListView
, состоящий из представлений EditText
. В сценарии говорится, что после редактирования текста в одной или нескольких строках мы должны нажать кнопку "закончить". Я использовал onFocusChanged
в представлении EditText
внутри ListView
, но после нажатия на завершение данные не сохраняются. Проблема была решена путем добавления
listView.clearFocus();
внутри onClickListener
для кнопки "закончить", и данные были успешно сохранены.
Ответ 10
Лучший способ - использовать метод по умолчанию clearFocus()
Вы знаете, как правильно решать коды в onTouchListener
?
Просто позвоните EditText.clearFocus()
. Он очистит фокус в last EditText
.
Ответ 11
Как показано в @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 Store при использовании меню переполнения элемента), необходимо проверить соответствие действительности. В противном случае вы получите NullPointerException
.
Ответ 12
Просто определите два свойства родительского элемента EditText
как:
android:clickable="true"
android:focusableInTouchMode="true"
Поэтому, когда пользователь коснется области EditText
, фокус будет удален, так как фокус будет перенесен в родительский вид.
Ответ 13
Для меня Ниже работало -
1. Добавление android:clickable="true"
и android:focusableInTouchMode="true"
в parentLayout
of EditText
i.e 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
в классе Activity и вставка функции 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. Добавление setOnFocusChangeListener
для EditText
EmployeeId.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard(v);
}
}
});