EditText, wyraźne skupienie na dotyku Na Zewnątrz

Mój layout zawiera ListView, SurfaceView i EditText. Kiedy klikam na EditText, otrzymuje fokus i wyskakuje klawiatura ekranowa. Kiedy klikam gdzieś poza EditText, nadal ma fokus (nie powinien). Myślę, że mógłbym ustawić OnTouchListener ' s na innych widokach w layout i ręcznie wyczyścić EditText'S focus. Ale wydaje się zbyt hakerski...

Mam również taką samą sytuację w innym widoku layout-list z różnymi typami elementów, z których niektóre mają EditText ' S wewnątrz. Działają tylko tak jak pisałem wyżej.

Zadanie polega na tym, aby EditText stracić ostrość, gdy użytkownik dotknie czegoś poza nim.

Widziałem podobne pytania tutaj, ale nie znalazłem żadnego rozwiązania...

Author: fawaad, 2011-01-28

13 answers

Próbowałem tych wszystkich rozwiązań. edc598 był najbliżej pracy, ale zdarzenia dotykowe nie uruchamiały się na innych Viewzawartych w układzie. W przypadku, gdyby ktoś potrzebował tego zachowania, To właśnie to zrobiłem:

Stworzyłem (niewidoczny) FrameLayout o nazwie touchInterceptor jako ostatni View w układzie, tak aby nakładał się na wszystko (edit: musisz również użyć RelativeLayout jako nadrzędnego układu i dać touchInterceptor fill_parent atrybuty). Wtedy użyłem aby przechwycić dotknięcia i określić, czy dotyk był na górze EditText, czy nie:

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

Zwróć false, aby pozwolić, aby dotyk przeszedł.

To jest hacky, ale to jedyna rzecz, która mi pomogła.
 58
Author: Ken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-12 18:53:00

Bazując na odpowiedzi Kena, oto najbardziej modułowe rozwiązanie do kopiowania i wklejania.

XML nie jest potrzebny.

Umieść go w swojej aktywności, a będzie on miał zastosowanie do wszystkich edycji, w tym do fragmentów w tej aktywności.

@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 );
}
 162
Author: zMan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-19 12:40:01

Dla nadrzędnego widoku EditText niech następujące 3 atrybuty będą " true":
klikalne, focusable, focusableInTouchMode .

Jeśli widok chce otrzymać ostrość, musi spełniać te 3 warunki.

Zobacz android.Widok :

public boolean onTouchEvent(MotionEvent event) {
    ...
    if (((viewFlags & CLICKABLE) == CLICKABLE || 
        (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
        ...
        if (isFocusable() && isFocusableInTouchMode()
            && !isFocused()) {
                focusTaken = requestFocus();
        }
        ...
    }
    ...
}
Mam nadzieję, że to pomoże.
 30
Author: suber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-21 14:39:01

Odpowiedź Kena działa, ale jest hacky. Jak wspomina PCAN w komentarzu odpowiedzi, to samo można zrobić z dispatchTouchEvent. To rozwiązanie jest czystsze, ponieważ pozwala uniknąć hakowania XML za pomocą przezroczystego, fałszywego FrameLayout. Oto jak to wygląda:

@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);
}
 13
Author: Mike Ortiz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-28 20:48:14

Po prostu umieść te właściwości w top most parent.

android:focusableInTouchMode="true"
android:clickable="true"
android:focusable="true" 
 7
Author: chandan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-09 04:18:15

Prawdopodobnie już znalazłeś odpowiedź na ten problem, ale szukałem, jak to rozwiązać i nadal nie mogę znaleźć dokładnie tego, czego szukałem, więc pomyślałem, że opublikuję go tutaj.

To co zrobiłem było następujące (To jest bardzo uogólnione, celem jest dać ci pomysł, jak postępować, kopiowanie i wklejanie całego kodu nie będzie działać O: D): {]}

Najpierw umieść edytowany tekst i inne widoki w programie owinięte pojedynczym widokiem. W moim przypadku użyłem LinearLayout do zawijania wszystkiego.

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

Następnie w kodzie musisz ustawić Touch Listener do głównego 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;
        }
    });
Mam nadzieję, że to pomoże niektórym ludziom. Albo przynajmniej pomaga im rozwiązać swój problem.
 4
Author: edc598,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-22 22:10:00

Naprawdę uważam, że jest to bardziej solidny sposób użycia getLocationOnScreen niż getGlobalVisibleRect. Bo mam problem. Istnieje Listview, który zawiera Edittext i ustawia ajustpan w aktywności. Znajduję getGlobalVisibleRect Zwraca wartość, która wygląda tak, że zawiera scrollY w nim, ale zdarzenie.getRawY jest zawsze przy ekranie. Poniższy kod działa dobrze.

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
Author: Victor Choy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-07 05:02:13

Aby utracić fokus po dotknięciu innego widoku, oba widoki powinny być ustawione jako widok.focusableInTouchMode (true).

Ale wydaje się, że używanie focusów w trybie dotykowym nie jest zalecane. Proszę spojrzeć tutaj: http://android-developers.blogspot.com/2008/12/touch-mode.html

 1
Author: forumercio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-28 14:03:04

Mam ListView składa się z EditText widoki. Scenariusz mówi, że po edycji tekstu w jednym lub kilku wierszach powinniśmy kliknąć na przycisk o nazwie "Zakończ". Użyłem onFocusChanged w widoku EditText wewnątrz listView, ale po kliknięciu na Zakończ dane nie są zapisywane. Problem został rozwiązany poprzez dodanie

listView.clearFocus();

Wewnątrz onClickListener dla przycisku "Zakończ" i dane zostały zapisane pomyślnie.

 1
Author: Muhannad A.Alhariri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-14 16:00:49

Po prostu zdefiniuj dwie właściwości rodzica tego EditText jako:

android:clickable="true"
android:focusableInTouchMode="true"

Więc gdy użytkownik dotknie poza EditText Obszar, fokus zostanie usunięty, ponieważ fokus zostanie przeniesiony do widoku nadrzędnego.

 1
Author: Dhruvam Gupta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-05 18:34:38

For me Below things Worked -

1.dodanie android:clickable="true" i android:focusableInTouchMode="true" do parentLayout z EditText tj. 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.nadpisywanie dispatchTouchEvent w klasie Activity i wstawianie hideKeyboard() funkcji

@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.dodanie setOnFocusChangeListener dla EditText

EmployeeId.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    hideKeyboard(v);
                }
            }
        });
 1
Author: Adarsh Gumashta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-09 14:49:14

Najlepszym sposobem jest użycie domyślnej metody clearFocus()

Wiesz jak rozwiązywać kody w onTouchListener prawda?

Po Prostu zadzwoń EditText.clearFocus(). Oczyści ostrość w last EditText.

 0
Author: Huy Tower,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-18 03:49:11

Jak zasugerował @ pcans, możesz to zrobić w swojej aktywności.

Tutaj dostajemy współrzędne dotyku i porównujemy je z granicami widoku. Jeśli dotyk jest wykonywany poza widokiem, zrób coś.

@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);
}

Nie jest również konieczne sprawdzanie istnienia i widoczności widoku, jeśli układ Twojej aktywności nie zmienia się w czasie działania (np. nie dodajesz fragmentów lub nie zastępujesz/usuwasz widoków z układu). Ale jeśli chcesz zamknąć (lub zrobić coś podobnego) niestandardowe menu kontekstowe (podobnie jak w Sklepie Google Play podczas korzystania z menu przelewu elementu) konieczne jest sprawdzenie istnienia widoku. W przeciwnym razie otrzymasz NullPointerException.

 0
Author: Sabre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-22 22:39:54