wake-up-neo.com

EditText, klarer Fokus auf Berührung außerhalb

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 ...

76
note173

Ich habe alle diese Lösungen ausprobiert. edc598 war am besten zu arbeiten, aber Touch-Events wurden nicht für andere Views 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.

61
Ken

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 );
}
178
zMan

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. 

32
suber

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);
}
14
Mike Ortiz

Setzen Sie diese Eigenschaften einfach in den obersten übergeordneten Bereich.

Android:focusableInTouchMode="true"
Android:clickable="true"
Android:focusable="true" 
12
chandan

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.

5
edc598

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())));
}
2
Victor Choy

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. 

1
Dhruvam Gupta

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. 

1

Für mich unten sachen gearbeitet 

1. Hinzufügen von Android:clickable="true" und Android:focusableInTouchMode="true" zur parentLayout von EditTexti.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);
                }
            }
        });
1
Adarsh Gumashta

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

1
forumercio

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.

0
Sabre

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.

0
Huy Tower

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));
0
Andrii