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. Lorsque je clique quelque part en dehors de EditText
, il a toujours le focus (cela ne devrait pas) . Je suppose que je pourrais configurer les OnTouchListener
sur les autres vues de la présentation et effacer manuellement le focus de EditText
. Mais semble trop hackish ...
J'ai également la même situation dans l'autre affichage de liste de présentation avec différents types d'éléments, dont certains contiennent des éléments EditText
. Ils agissent comme je l'ai écrit ci-dessus.
La tâche consiste à faire en sorte que EditText
ne se concentre plus lorsque l'utilisateur touche quelque chose en dehors de celle-ci.
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 les autres View
s contenus dans la présentation. Au cas où quelqu'un aurait besoin de ce comportement, voici ce que j'ai fini par faire:
J'ai créé une variable (invisible) FrameLayout
appelée touchInterceptor en tant que dernière View
dans la présentation, de sorte qu'elle recouvre tout ( edit: vous devez également utiliser une RelativeLayout
comme disposition parent et donner le touchInterceptorfill_parent
attributs). Ensuite, je l'ai utilisé pour intercepter les contacts et déterminer si le contact était au-dessus de 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;
}
});
Renvoie false pour laisser la manipulation tactile échouer.
C'est hacky, mais c'est la seule chose qui a fonctionné pour moi.
S'appuyant sur la réponse de Ken, voici la solution de copier-coller la plus modulaire.
Pas de XML nécessaire.
Mettez-le dans votre activité et cela s'appliquera à tous les EditTexts, y compris ceux contenus 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 parent de EditText, laissez les 3 attributs suivants être " true ":
clickable , focusable , focusableInTouchMode .
Si une vue souhaite 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 ça aide.
La réponse de Ken fonctionne, mais c'est hacky. Comme le dit le commentaire de la réponse, pcans 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 ç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);
}
Il suffit de mettre ces propriétés dans la partie la plus 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 un moyen de résoudre ce problème et je ne parviens toujours pas à trouver exactement ce que je cherchais.
Voici ce que j’ai fait (c’est très général, le but est de vous donner une idée de la marche à suivre, copier et coller tout le code ne fonctionnera pas O: D):
Commencez par insérer le texte EditText et les autres vues de votre programme dans une seule vue. Dans mon cas, j'ai utilisé un LinearLayout pour tout emballer.
<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>
Ensuite, dans votre code, vous devez définir un Touch Listener 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, cela 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 existe une liste qui contient du texte Edittext et ajustpan
dans l'activité. Je trouve que getGlobalVisibleRect
retourne une valeur qui ressemble à 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())));
}
Définissez simplement deux propriétés de parent de cette EditText
comme suit:
Android:clickable="true"
Android:focusableInTouchMode="true"
Ainsi, lorsque l'utilisateur touchera en dehors de la zone EditText
, la focalisation sera supprimée car elle sera transférée à la vue parent.
J'ai une ListView
composée de EditText
vues. Le scénario dit qu'après avoir édité du texte dans une ou plusieurs rangées, il faut cliquer sur un bouton appelé "terminer". J'ai utilisé onFocusChanged
dans la vue EditText
à l'intérieur de 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 la onClickListener
du bouton "terminer" et les données ont été enregistrées avec succès.
Pour moi, ci-dessous, les choses ont fonctionné -
1. Ajout de Android:clickable="true"
et Android:focusableInTouchMode="true"
à la 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. Remplacer dispatchTouchEvent
dans la classe d'activité 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. Ajouter setOnFocusChangeListener
pour EditText
EmployeeId.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard(v);
}
}
});
Pour perdre le focus lorsque vous touchez une autre vue, les deux vues doivent être définies comme suit: view.focusableInTouchMode (true).
Mais il semble que l'utilisation de focus en mode tactile ne soit pas recommandée . S'il vous plaît jetez un oeil ici: http://Android-developers.blogspot.com/2008/12/touch-mode.html
Comme @pcans l’a suggéré, vous pouvez supprimer cette dispatchTouchEvent(MotionEvent event)
dans votre activité.
Ici, nous obtenons les coordonnées tactiles et les comparant pour afficher les limites. Si le toucher est effectué en dehors d'une 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'existence et la visibilité des vues si la présentation de votre activité ne change pas pendant l'exécution (par exemple, vous n'ajoutez pas de fragments, ne remplacez pas/ne supprimez pas les vues de la présentation). Toutefois, si vous souhaitez fermer (ou faire quelque chose de similaire) un menu contextuel personnalisé (comme dans Google Play Store lorsque vous utilisez le menu de dépassement de capacité de l'élément), vous devez vérifier l'existence de la vue. Sinon, vous obtiendrez une NullPointerException
.
La meilleure façon est d'utiliser la méthode par défaut clearFocus()
Vous savez comment résoudre les codes dans onTouchListener
à droite?
Il suffit d'appeler EditText.clearFocus()
. Cela effacera le focus dans last EditText
.
Ce simple extrait de code fait ce que vous voulez
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));