Mein Layout enthält ListView
, SurfaceView
und EditText
. Wenn ich auf EditText
klicke, wird der Fokus angezeigt und die Bildschirmtastatur wird eingeblendet. Wenn ich irgendwo außerhalb der EditText
klicke, hat es immer noch den Fokus (sollte nicht) ..__ Ich denke, ich könnte OnTouchListener
in den anderen Ansichten im Layout einrichten und den Fokus der EditText
manuell löschen. Scheint aber zu hackig ...
Ich habe auch die gleiche Situation in der anderen Layout-Listenansicht mit verschiedenen Arten von Elementen, von denen einige EditText
enthalten. Sie verhalten sich genauso wie ich oben geschrieben habe.
Die Aufgabe besteht darin, EditText
den Fokus zu verlieren, wenn der Benutzer etwas außerhalb berührt.
Ich habe ähnliche Fragen hier gesehen, aber keine Lösung gefunden ...
Ich habe alle diese Lösungen ausprobiert. edc598 war am besten zu arbeiten, aber Touch-Events wurden nicht für andere View
s ausgelöst, die im Layout enthalten waren. Für den Fall, dass jemand dieses Verhalten benötigt, habe ich Folgendes getan:
Ich habe eine (unsichtbare) FrameLayout
mit dem Namen touchInterceptor als letzte View
im Layout erstellt, sodass sie alles überlagert ( edit: Sie müssen auch eine RelativeLayout
als übergeordnetes Layout verwenden und den touchInterceptorfill_parent
Attribute). Dann habe ich es benutzt, um Berührungen abzufangen und festzustellen, ob die Berührung auf der Variablen EditText
war oder nicht:
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;
}
});
Geben Sie false zurück, damit die Berührungsbehandlung durchfällt.
Es ist hackig, aber es war das einzige, was für mich funktionierte.
Aufbauend auf Ken's Antwort ist hier die modularste Copy-and-Paste-Lösung.
Kein XML benötigt.
Fügen Sie es in Ihre Aktivität ein und es gilt für alle EditTexte, einschließlich derjenigen innerhalb von Fragmenten innerhalb dieser Aktivität.
@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 );
}
Lassen Sie für die übergeordnete Ansicht von EditText die folgenden drei Attribute " true " sein:
klickbare , fokussierbare , focusableInTouchMode .
Wenn eine Ansicht den Fokus erhalten möchte, muss sie diese 3 Bedingungen erfüllen.
Siehe Android.view :
public boolean onTouchEvent(MotionEvent event) {
...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
...
if (isFocusable() && isFocusableInTouchMode()
&& !isFocused()) {
focusTaken = requestFocus();
}
...
}
...
}
Ich hoffe es hilft.
Kens Antwort funktioniert, aber es ist hackig. Während pcans im Kommentar der Antwort darauf anspielt, könnte das gleiche mit dispatchTouchEvent geschehen. Diese Lösung ist sauberer, da das XML nicht mit einem transparenten, blinden FrameLayout gehackt werden muss. So sieht das aus:
@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);
}
Setzen Sie diese Eigenschaften einfach in den obersten übergeordneten Bereich.
Android:focusableInTouchMode="true"
Android:clickable="true"
Android:focusable="true"
Sie haben wahrscheinlich bereits die Antwort auf dieses Problem gefunden, aber ich habe nach Lösungen gesucht und kann immer noch nicht genau das finden, wonach ich gesucht habe. Ich dachte mir, ich würde es hier posten.
Was ich getan habe, war folgendes (dies ist sehr verallgemeinert, mit dem Ziel, Ihnen eine Vorstellung davon zu geben, wie Sie fortfahren, das Kopieren und Einfügen des gesamten Codes wird nicht funktionieren):
Lassen Sie zunächst den EditText und alle anderen Ansichten, die Sie in Ihrem Programm verwenden möchten, in eine einzige Ansicht einbetten. In meinem Fall habe ich ein LinearLayout verwendet, um alles einzuwickeln.
<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>
Dann müssen Sie in Ihrem Code einen Touch Listener auf Ihr LinearLayout einstellen.
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;
}
});
Ich hoffe, das hilft einigen Leuten. Oder hilft ihnen zumindest, ihr Problem zu lösen.
Ich denke wirklich, dass es eine robustere Methode ist, getLocationOnScreen
zu verwenden als getGlobalVisibleRect
. Weil ich auf ein Problem stoße. Es gibt eine Listenansicht, die einige Edittext und und ajustpan
in der Aktivität enthält. Ich finde, dass getGlobalVisibleRect
einen Wert zurückgibt, der aussieht, als würde scrollY darin enthalten, aber event.getRawY ist immer am Bildschirm. Der folgende Code funktioniert gut.
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())));
}
Definieren Sie einfach zwei Eigenschaften des übergeordneten Elements von EditText
als:
Android:clickable="true"
Android:focusableInTouchMode="true"
Wenn sich der Benutzer außerhalb des EditText
-Bereichs befindet, wird der Fokus entfernt, da der Fokus auf die übergeordnete Ansicht übertragen wird.
Ich habe eine ListView
, die aus EditText
Ansichten besteht. Das Szenario besagt, dass wir nach der Bearbeitung von Text in einer oder mehreren Zeilen auf eine Schaltfläche mit dem Namen "Fertig stellen" klicken sollten. Ich habe onFocusChanged
in der EditText
-Ansicht in listView
verwendet, aber nach dem Klicken auf Fertigstellen werden die Daten nicht gespeichert. Das Problem wurde durch Hinzufügen gelöst
listView.clearFocus();
innerhalb der onClickListener
für die Schaltfläche "Fertig stellen" und die Daten wurden erfolgreich gespeichert.
Für mich unten sachen gearbeitet
1. Hinzufügen von Android:clickable="true"
und Android:focusableInTouchMode="true"
zur parentLayout
von 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. Überschreiben von dispatchTouchEvent
in der Aktivitätsklasse und Einfügen von hideKeyboard()
function
@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. Hinzufügen von setOnFocusChangeListener
für EditText
EmployeeId.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard(v);
}
}
});
Um den Fokus zu verlieren, wenn eine andere Ansicht berührt wird, sollten beide Ansichten als view.focusableInTouchMode (true) festgelegt werden.
Es scheint jedoch, dass die Verwendung von Fokussen im Touch-Modus nicht empfohlen wird. Bitte schauen Sie hier nach: http://Android-developers.blogspot.com/2008/12/touch-mode.html
Wie von @pcans vorgeschlagen, können Sie dies dispatchTouchEvent(MotionEvent event)
in Ihrer Aktivität überschreiben.
Hier bekommen wir die Berührungskoordinaten und vergleichen sie mit Sichtgrenzen. Wenn die Berührung außerhalb einer Ansicht erfolgt, tun Sie etwas.
@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);
}
Es ist auch nicht erforderlich, das Vorhandensein und die Sichtbarkeit der Ansichten zu überprüfen, wenn sich das Layout Ihrer Aktivität während der Laufzeit nicht ändert (z. B. Sie fügen keine Fragmente hinzu und ersetzen oder entfernen Ansichten nicht aus dem Layout). Wenn Sie jedoch das benutzerdefinierte Kontextmenü schließen möchten (oder etwas ähnliches tun möchten) (z. B. im Google Play Store, wenn Sie das Überlaufmenü des Elements verwenden), müssen Sie die Ansicht der vorhandenen Elemente überprüfen. Andernfalls erhalten Sie eine NullPointerException
.
Am besten verwenden Sie die Standardmethode clearFocus()
Sie wissen, wie man Codes in onTouchListener
richtig löst?
Rufen Sie einfach EditText.clearFocus()
auf. In last EditText
wird der Fokus klarer.
Dieses einfache Codefragment macht, was Sie wollen
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));