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...
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.
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 );
}
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.
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);
}
mettez ces propriétés dans le haut plus parent.
android:focusableInTouchMode="true"
android:clickable="true"
android:focusable="true"
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.
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())));
}
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
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.
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.
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);
}
}
});
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
.
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
.