EditText, clairement l'accent sur le contact à l'extérieur

ma mise en page contient ListView , SurfaceView et EditText . Quand je clique sur le EditText , il reçoit le focus et le clavier à l'écran apparaît. Quand je clique quelque part en dehors du EditText , il a toujours le focus (il ne devrait pas). Je pense que je pourrais mettre en place OnTouchListener sur les autres vues dans layout et effacer manuellement la mise au point EditText . Mais ça semble trop hackish...

j'ai également la même situation dans l'autre vue de la différents types d'articles, dont certains ont EditText à l'intérieur. Ils agissent comme je l'ai écrit plus haut.

la tâche consiste à faire en sorte que EditText perde sa concentration lorsque l'utilisateur touche quelque chose en dehors de celle-ci.

j'ai vu des questions similaires ici, mais n'ai pas trouvé de solution...

69
demandé sur fawaad 2011-01-28 16:18:32

13 réponses

j'ai essayé toutes ces solutions. les edc598's étaient les plus proches du travail, mais les événements tactiles n'ont pas déclenché d'autres View contenus dans le layout. Si quelqu'un a besoin de ce comportement, c'est ce que j'ai fini par faire:

j'ai créé un (invisible) FrameLayout appelé touchInterceptor comme le dernier View dans le layout de sorte qu'il recouvre tout ( edit: vous devez également utiliser un RelativeLayout comme le layout parent et donner les attributs touchInterceptor fill_parent ). Puis je l'ai utilisé pour intercepter les touches et déterminer si le contact était au-dessus du EditText ou non:

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 laisser le contact de la manipulation de tomber.

C'est hacky, mais c'est la seule chose qui a fonctionné pour moi.

58
répondu Ken 2012-01-12 18:53:00

en S'appuyant sur la réponse de Ken, voici la solution de copier-coller la plus modulaire.

pas besoin de XML.

mettez-le dans votre activité et il s'appliquera à tous les textes D'édition, y compris ceux inclus dans les fragments de cette activité.

@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
répondu zMan 2018-06-19 12:40:01

pour la vue parent D'EditText, que les 3 attributs suivants soient" true ":

clickable , focusable , focusable intouchmode .

si une vue veut recevoir la mise au point, elle doit satisfaire ces 3 conditions.

voir android.view :

public boolean onTouchEvent(MotionEvent event) {
    ...
    if (((viewFlags & CLICKABLE) == CLICKABLE || 
        (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
        ...
        if (isFocusable() && isFocusableInTouchMode()
            && !isFocused()) {
                focusTaken = requestFocus();
        }
        ...
    }
    ...
}

J'espère que ça aidera.

30
répondu suber 2015-04-21 14:39:01

la réponse de Ken fonctionne, mais c'est hacky. Comme pcans fait allusion dans le commentaire de la réponse, la même chose pourrait être faite avec dispatchTouchEvent. Cette solution est plus propre car elle évite d'avoir à hacker le XML avec un FrameLayout transparent et factice. Voici à quoi ça ressemble:

@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
répondu Mike Ortiz 2017-11-28 20:48:14

mettez ces propriétés dans le haut plus parent.

android:focusableInTouchMode="true"
android:clickable="true"
android:focusable="true" 
7
répondu chandan 2017-09-09 04:18:15

vous avez probablement déjà trouvé la réponse à ce problème, mais j'ai cherché comment résoudre ce problème et je ne peux toujours pas trouver exactement ce que je cherchais donc j'ai pensé le poster ici.

ce que j'ai fait était le suivant (c'est très généralisé, le but est de vous donner une idée de comment procéder, copier et coller tout le code ne fonctionnera pas O:d):

tout D'abord avoir le texte édité et toutes les autres vues que vous voulez dans votre programme Enveloppé par une vue unique. Dans mon cas, j'ai utilisé un affichage en ligne pour tout envelopper.

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

alors dans votre code vous devez définir un écouteur tactile à votre LinearLayout principal.

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

j'espère que cela aidera certaines personnes. Ou au moins les aide à résoudre leur problème.

4
répondu edc598 2011-12-22 22:10:00

je pense vraiment que c'est une façon plus robuste d'utiliser getLocationOnScreen que getGlobalVisibleRect . Parce que je rencontre un problème. Il y a un Listview qui contient du texte édité et et définit ajustpan dans l'activité. Je trouve getGlobalVisibleRect retourner une valeur qui ressemble y compris la scrollY, mais l'événement.getRawY est toujours à l'écran. Le code ci-dessous fonctionne bien.

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
répondu Victor Choy 2017-05-07 05:02:13

pour perdre la mise au point lorsque l'autre vue est touchée , les deux vues doivent être positionnées comme vue.focusableInTouchMode (true).

mais il semble que l'utilisation des focus en mode touch ne soit pas recommandée. Veuillez prendre un coup d'oeil ici: http://android-developers.blogspot.com/2008/12/touch-mode.html

1
répondu forumercio 2011-01-28 14:03:04

j'ai un ListView composé de EditText vues. Le scénario dit qu'après avoir édité du texte dans une ou plusieurs lignes nous devrions cliquer sur un bouton appelé "terminer". J'ai utilisé onFocusChanged sur le EditText vue à l'intérieur de listView mais après avoir cliqué sur Terminer les données ne sont pas sauvegardées. Le problème a été résolu en ajoutant

listView.clearFocus();

à l'intérieur du onClickListener pour le bouton" finish " et les données ont été sauvegardées avec succès.

1
répondu Muhannad A.Alhariri 2015-09-14 16:00:49

définit simplement deux propriétés de parent de ce EditText comme:

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

Ainsi, lorsque l'utilisateur va toucher à l'extérieur de EditText , l'accent sera supprimé car le focus sera transféré à la vue parent.

1
répondu Dhruvam Gupta 2017-09-05 18:34:38

Pour Moi, les choses ci-Dessous Travaillé -

1. ajouter android:clickable="true" et android:focusableInTouchMode="true" au parentLayout de 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. supérieur dispatchTouchEvent dans la classe d'activité et en insérant hideKeyboard() fonction

@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. ajout de setOnFocusChangeListener pour EditText

EmployeeId.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    hideKeyboard(v);
                }
            }
        });
1
répondu Adarsh Gumashta 2017-12-09 14:49:14

la meilleure façon est d'utiliser la méthode par défaut clearFocus()

vous savez comment résoudre les codes dans onTouchListener n'est-ce pas?

il suffit d'appeler EditText.clearFocus() . Il sera clairement mis au point dans last EditText .

0
répondu Huy Tower 2014-07-18 03:49:11

comme @pcans a suggéré que vous pouvez faire ce dispatchTouchEvent(MotionEvent event) dans votre activité.

ici nous obtenons les coordonnées de contact et les comparons aux limites de vue. Si le toucher est effectué en dehors d'une vue alors faire quelque chose.

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

de plus, il n'est pas nécessaire de vérifier l'existance et la visibilité de la vue si la disposition de votre activité ne change pas pendant l'exécution (par exemple, vous n'ajoutez pas de fragments ou ne remplacez pas/supprimez des vues de la disposition). Mais si vous voulez pour fermer (ou faire quelque chose de similaire) le menu contextuel personnalisé (comme dans la boutique Google Play en utilisant le menu overflow de l'élément) il est nécessaire de vérifier l'existence de la vue. Sinon, vous obtiendrez un NullPointerException .

0
répondu Sabre 2015-03-22 22:39:54