EditText, focus clair sur le toucher à l’extérieur

Ma mise en page contient ListView , SurfaceView et EditText . Lorsque je clique sur 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 suppose que je pourrais configurer OnTouchListener sur les autres vues de la mise en page et effacer manuellement le EditText de EditText . Mais semble trop pirate …

J’ai aussi la même situation dans l ‘autre vue mise en page avec différents types d’ éléments, dont certains contiennent EditText . Ils agissent comme j’ai écrit ci-dessus.

La tâche consiste à faire en sorte EditText perd le focus lorsque l’utilisateur touche quelque chose en dehors de lui.

J’ai vu des questions similaires ici, mais je n’ai trouvé aucune solution …

J’ai essayé toutes ces solutions. edc598 était le plus proche du travail, mais les événements tactiles ne se sont pas déclenchés sur d’autres View contenues dans la mise en page. Si quelqu’un a besoin de ce comportement, c’est ce que j’ai fini par faire:

J’ai créé un FrameLayout (invisible) appelé touchInterceptor en tant que dernière View de la mise en page pour qu’il recouvre tout ( edit: vous devez également utiliser une mise en page RelativeLayout comme atsortingbut parent et atsortingbuer les atsortingbuts touch_terceptor fill_parent ). Ensuite, je l’ai utilisé pour intercepter les contacts et déterminer si le toucher était en haut 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; } }); 

Retournez false pour laisser passer la manipulation tactile.

C’est pirate, mais c’est la seule chose qui a fonctionné pour moi.

En s’appuyant sur la réponse de Ken, voici la solution de copier-coller la plus modulaire.

Aucun XML nécessaire.

Mettez-le dans votre activité et cela s’appliquera à tous les EditTexts, y compris ceux qui se trouvent dans des 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 ); } 

Pour la vue parente de EditText, laissez les trois atsortingbuts suivants être ” true “:
cliquable , focalisable , focusableInTouchMode .

Si une vue veut recevoir le focus, 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 cela aide.

La réponse de Ken fonctionne, mais c’est pirate. Comme le fait remarquer pcans dans le commentaire de la réponse, on pourrait faire la même chose avec dispatchTouchEvent. Cette solution est plus propre car elle évite de devoir pirater le XML avec un FrameLayout factice transparent. Voici à quoi cela 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); } 

Il suffit de mettre ces propriétés dans le plus haut parent.

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

Vous avez probablement déjà trouvé la réponse à ce problème, mais je cherchais à résoudre ce problème et je ne trouve toujours pas exactement ce que je cherchais, alors je me suis dit que je le posterais ici.

Ce que j’ai fait était le suivant (ceci est très généralisé, le but est de vous donner une idée de la manière de procéder, copier et coller tout le code ne fonctionnera pas O: D):

Commencez par placer le EditText et toutes les autres vues de votre programme dans une seule vue. Dans mon cas, j’ai utilisé un LinearLayout pour tout emballer.

      

Ensuite, dans votre code, vous devez définir un écouteur tactile sur 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 du moins les aide à résoudre leur problème.

Je pense vraiment que c’est un moyen plus robuste d’utiliser getLocationOnScreen que getGlobalVisibleRect . Parce que je rencontre un problème. Il y a une Listview qui contient certains Edittext et qui définit ajustpan dans l’activité. Je trouve que getGlobalVisibleRect renvoie une valeur qui semble inclure le scrollY, mais event.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()))); } 

Pour perdre le focus quand une autre vue est touchée, les deux vues doivent être définies comme view.focusableInTouchMode (true).

Mais il semble que l’utilisation des focus en mode tactile n’est pas recommandée. S’il vous plaît jeter un oeil ici: http://android-developers.blogspot.com/2008/12/touch-mode.html

J’ai un ListView composé de vues EditText . Le scénario dit qu’après avoir édité du texte sur une ou plusieurs lignes, il faut cliquer sur un bouton appelé “terminer”. J’ai utilisé onFocusChanged sur la vue EditText dans listView mais après avoir cliqué sur Terminer, les données ne sont pas enregistrées. Le problème a été résolu en ajoutant

 listView.clearFocus(); 

dans le onClickListener pour le bouton “finish” et les données ont été enregistrées avec succès.

Définissez simplement deux propriétés de parent de ce EditText comme:

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

Ainsi, lorsque l’utilisateur touchera en dehors de la zone EditText , le focus sera supprimé car le focus sera transféré à la vue parente.

Pour moi ci-dessous les choses ont fonctionné –

1. Ajouter android:clickable="true" et android:focusableInTouchMode="true" au parentLayout de EditText exemple android.support.design.widget.TextInputLayout

    

2. Remplacer dispatchTouchEvent dans la classe Activity et insérer la fonction 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. Ajout de setOnFocusChangeListener pour EditText

 EmployeeId.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { hideKeyboard(v); } } }); 

Le meilleur moyen est d’utiliser la méthode par défaut clearFocus()

Vous savez comment résoudre les codes dans onTouchListener n’est-ce pas?

Appelez simplement EditText.clearFocus() . Cela last EditText focus dans le last EditText .

En tant que @pcans, vous pouvez effectuer cette dispatchTouchEvent(MotionEvent event) dans votre activité.

Ici, nous obtenons les coordonnées tactiles et les comparons pour voir les limites. Si le toucher est effectué hors de la vue, faites 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 / supprimez pas de vues de la présentation). Mais si vous voulez fermer (ou faire quelque chose de similaire) un menu contextuel personnalisé (comme dans Google Play Store lorsque vous utilisez le menu de débordement de l'élément), il est nécessaire de vérifier l'existance de la vue. Sinon, vous obtiendrez une NullPointerException .