Comment supprimer le focus sans définir le focus sur un autre contrôle?


95

J'aime que mes interfaces utilisateur soient intuitives; chaque écran doit guider naturellement et discrètement l'utilisateur vers l'étape suivante de l'application. À part cela, je m'efforce de rendre les choses aussi déroutantes et déroutantes que possible.

Je rigole :-)

J'ai trois TableRows, chacun contenant un contrôle EditText en lecture seule et non focalisable, puis un bouton à sa droite. Chaque bouton démarre la même activité mais avec un argument différent. L'utilisateur y fait une sélection et la sous-activité se termine, en remplissant la sélection appropriée EditTextavec la sélection de l'utilisateur.

C'est le mécanisme classique des valeurs en cascade; chaque sélection réduit les options disponibles pour la sélection suivante, etc. Ainsi, je désactive les deux contrôles sur chacune des lignes suivantes jusqu'à ce que EditText sur la ligne actuelle contienne une valeur.

Je dois faire l'une des deux choses suivantes, dans cet ordre de préférence:

  1. Lorsqu'un bouton est cliqué, supprimez immédiatement le focus sans définir le focus sur un bouton différent
  2. Mettre le focus sur le premier bouton lorsque l'activité commence

Le problème se manifeste après le retour de la sous-activité; le bouton sur lequel vous avez cliqué conserve le focus.

Re: # 1 ci-dessus - Il ne semble pas y avoir de removeFocus()méthode ou quelque chose de similaire

Re: # 2 ci-dessus - Je peux utiliser requestFocus()pour mettre le focus sur le bouton de la ligne suivante, et cela fonctionne après le retour de la sous-activité, mais pour une raison quelconque, cela ne fonctionne pas dans l'activité parent onCreate().

J'ai besoin de cohérence de l'interface utilisateur dans les deux sens - soit aucun bouton n'a le focus une fois la sous-activité terminée, soit chaque bouton reçoit le focus en fonction de sa place dans le flux logique, y compris le tout premier (et seul) bouton actif avant toute sélection.

Réponses:


173

Utiliser clearFocus () ne semblait pas non plus fonctionner pour moi comme vous l'avez trouvé (vu dans les commentaires d'une autre réponse), mais ce qui a fonctionné pour moi à la fin était d'ajouter:

<LinearLayout 
    android:id="@+id/my_layout" 
    android:focusable="true" 
    android:focusableInTouchMode="true" ...>

à ma vue de mise en page de très haut niveau (une mise en page linéaire). Pour supprimer le focus de tous les Buttons / EditTexts, etc., vous pouvez alors simplement faire

LinearLayout myLayout = (LinearLayout) activity.findViewById(R.id.my_layout);
myLayout.requestFocus();

La demande de mise au point ne faisait rien à moins que je ne définisse la vue pour être focalisable.


Pendant un instant, j'ai pensé que cela pourrait fonctionner. "Que c'est beau dans sa simplicité!" Je me suis dit. Hélas, cela ne devait pas être. Même lorsque je supprime requestFocus (), le bouton de la ligne suivante obtient le focus. Ce serait OK comme deuxième préférence, mais seulement si je peux d'une manière ou d'une autre mettre le focus sur le premier bouton dans onCreate ().
InteXX

@InteXX: Je suis tombé à nouveau sur ce problème aujourd'hui et j'ai trouvé une solution. Je sais que vous avez emprunté un itinéraire différent maintenant, mais essayez-le si jamais vous vous retrouvez dans le même bateau!
actionshrimp

"Je sais que vous avez emprunté une voie différente maintenant" Il n'est pas encore trop tard pour reculer :-) Je vais essayer et vous le faire savoir, merci.
InteXX

Zut. Presque, mais pas tout à fait. Oui, cela enlève le focus sur le dernier bouton cliqué, mais cela empêche également TOUT bouton de se concentrer. Cela signifierait que mes utilisateurs non tactiles ne pourraient pas cliquer sur un bouton. Cela fait-il la même chose pour vous?
InteXX

6
Cela ne fonctionnera pas sur ScrollView. Si votre vue de dessus est ScrollView, utilisez-la sur son enfant.
Uroš Podkrižnik

79

Ancienne question, mais je suis tombée dessus quand j'ai eu un problème similaire et j'ai pensé que je partagerais ce que j'ai fini par faire.

La vue qui a gagné l'attention était différente à chaque fois, j'ai donc utilisé le très générique:

View current = getCurrentFocus();
if (current != null) current.clearFocus();

12
Une manière élégante de faire cela. On pourrait vouloir vérifier pour null une valeur de getCurrentFocus () avant d'appeler clearFocus ()
Vering

4
+1 Oui, vous devrez lancer ce test nul! - Pourquoi Java ne peut-il pas simplement implémenter un opérateur de parcours sécurisé? Ce serait aussi simple que getCurrentFocus()?.clearFocus();, si beau et élégant: - /
Levite

1
@willlma: Oui, c'est exactement ce que je voulais dire.
Vering

5
@Levit Très Swift. :-) Mais même si Java implémentait un tel opérateur, il faudrait encore 4 ans à Android pour rattraper son retard.
Greg Brown

2
@Levit, vous l'avez probablement déjà trouvé, mais Kotlin.
prodaea le

9
  1. Vous pouvez utiliser View.clearFocus().

  2. Utilisez View.requestFocus()appelé de onResume().


@siliconeagle: Maintenant, c'est TRÈS étrange. (Merci pour le pointeur vers clearFocus () btw - je n'étais pas au courant de cela.) Lorsque je clique sur le premier bouton, fais une sélection, puis reviens, le premier bouton ne peut plus recevoir le focus. Je ne règle pas le focus pour ce bouton n'importe où dans mon code. J'aimerais vous montrer mon code, mais il n'y a pas assez de caractères disponibles dans cette fenêtre d'édition et étant nouveau dans SO, je ne suis pas sûr si je devrais entrer une autre réponse juste pour pouvoir publier le code.
InteXX

@siliconeagle: Malheureusement, clearFocus () ne fonctionne pas - les boutons conservent le focus lorsque la sous-activité revient.
InteXX

@siliconeagle: OK, peu importe ce premier commentaire. J'utilisais setFocusable (false) dans l'événement de clic du bouton et j'ai oublié de le réactiver après le retour de la sous-activité. clearFocus () n'a toujours aucun effet - je me demande pourquoi? Je l'appelle sur tous les boutons de onActivityResult (), mais le bouton cliqué conserve toujours le focus. Impair.
InteXX

@siliconeagle: @actionshrimp: J'ai décidé de supprimer toutes les commandes et paramètres liés à la mise au point. Mon intention initiale était d'éviter de me concentrer sur les boutons désactivés, mais si c'est ce que cela coûte, cela n'en vaut pas la peine. Sans requestFocus (), clearFocus () ou setFocusable (), aucun bouton ne conserve le focus après le retour de la sous-activité. C'est le moindre de deux charançons - je suppose que je vais devoir vivre avec la possibilité pour l'utilisateur de mettre le focus sur un bouton désactivé.
InteXX

les éléments désactivés ne sont pas focalisables AFAIK ... utilisez setEnabled (false)
siliconeagle

8

android:descendantFocusability="beforeDescendants"

l'utilisation des éléments suivants dans l'activité avec certaines options de mise en page ci-dessous semble fonctionner comme vous le souhaitez.

 getWindow().getDecorView().findViewById(android.R.id.content).clearFocus();

en relation avec les paramètres suivants sur la vue racine.

<?xml
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:descendantFocusability="beforeDescendants" />

https://developer.android.com/reference/android/view/ViewGroup#attr_android:descendantFocusability

Répondez grâce à: https://forums.xamarin.com/discussion/1856/how-to-disable-auto-focus-on-edit-text

À propos de windowSoftInputMode

Il y a encore un autre point de discorde dont il faut être conscient. Par défaut, Android attribuera automatiquement le focus initial au premier EditText ou au contrôle focusable de votre activité. Il s'ensuit naturellement que InputMethod (généralement le clavier virtuel) répondra à l'événement de focus en se montrant. L'attribut windowSoftInputMode dans AndroidManifest.xml, lorsqu'il est défini sur stateAlwaysHidden, indique au clavier d'ignorer ce focus initial attribué automatiquement.

<activity
    android:name=".MyActivity"
    android:windowSoftInputMode="stateAlwaysHidden"/>

grande référence


6
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          android:id="@+id/ll_root_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical">

LinearLayout llRootView = findViewBindId(R.id.ll_root_view);
llRootView.clearFocus();

J'utilise ceci lorsque j'ai déjà terminé de mettre à jour les informations de profil et de supprimer tout le focus d'EditText dans ma mise en page

====> Mise à jour: Dans le contenu de la mise en page parent, ma ligne d'ajout EditText:

android:focusableInTouchMode="true"


2

Tout d'abord, cela fonctionnera à 100% ........

  1. Créer une onResume()méthode.
  2. À l'intérieur de cela, onResume()retrouvez la vue qui se concentre encore et encore findViewById().
  3. À l'intérieur de cet onResume()ensemble requestFocus()à cette vue.
  4. À l'intérieur de cet onResume()ensemble clearFocusà cette vue.
  5. Allez dans xml de la même mise en page et trouvez la vue de dessus sur laquelle vous voulez vous concentrer et définir focusablevrai et focusableInTuchvrai.
  6. À l'intérieur de cela, onResume()trouvez la vue de dessus ci-dessus parfindViewById
  7. À l'intérieur de cet onResume()ensemble requestFocus()à cette vue à la fin.
  8. Et maintenant profitez ......

2

J'ai essayé de désactiver et d'activer la mise au point pour la vue et cela a fonctionné pour moi (la mise au point a été réinitialisée):

focusedView.setFocusable(false);
focusedView.setFocusableInTouchMode(false);
focusedView.setFocusable(true);
focusedView.setFocusableInTouchMode(true);

1

Vous pouvez essayer de désactiver la capacité de l'activité principale à enregistrer son état (en lui faisant ainsi oublier quel contrôle avait du texte et ce qui avait le focus). Vous aurez besoin d'un autre moyen de vous souvenir de ce que vos EditText ont et de les repeupler surResume (). Lancez vos sous-activités avec startActivityForResult () et créez un gestionnaire onActivityResult () dans votre activité principale qui mettra à jour correctement les EditText. De cette façon, vous pouvez définir le bouton approprié que vous voulez focaliser surResume () en même temps que vous remplissez le EditText en utilisant un myButton.post (new Runnable () {run () {myButton.requestFocus ();}});

La méthode View.post () est utile pour définir le focus initialement car ce runnable sera exécuté après la création de la fenêtre et le stabilisation des choses, permettant au mécanisme de focus de fonctionner correctement à ce moment-là. Essayer de définir le focus pendant onCreate / Start / Resume () pose généralement des problèmes, j'ai trouvé.

Veuillez noter que c'est pseudo-code et non testé, mais c'est une direction possible que vous pourriez essayer.


Après réflexion ... essayez d'abord d'utiliser les éléments View.post () au lieu de désactiver l'état enregistré de l'activité. Cela pourrait faire l'affaire pour vous tout seul.
Uncle Code Monkey

Hélas, je n'ai plus de moyen facile de tester cela pour nous. Depuis, j'ai décidé d'abandonner le modèle de code natif et d'opter pour HTML5 / CSS3 pour les applications mobiles, et j'ai donc réorganisé mon système en conséquence. Mais merci pour ce qui semble être une réponse très approfondie. Je l'ai voté.
InteXX

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

Ajoutez-les à votre ViewGroup qui inclut votre EditTextView. Cela fonctionne correctement avec ma disposition des contraintes. J'espère que cette aide


0

Essayez ce qui suit (appelez clearAllEditTextFocuses();)

private final boolean clearAllEditTextFocuses() {
    View v = getCurrentFocus();
    if(v instanceof EditText) {
        final FocusedEditTextItems list = new FocusedEditTextItems();
        list.addAndClearFocus((EditText) v);

        //Focus von allen EditTexten entfernen
        boolean repeat = true;
        do {
            v = getCurrentFocus();
            if(v instanceof EditText) {
                if(list.containsView(v))
                    repeat = false;
                else list.addAndClearFocus((EditText) v);
            } else repeat = false;
        } while(repeat);

        final boolean result = !(v instanceof EditText);
        //Focus wieder setzen
        list.reset();
        return result;
    } else return false;
}

private final static class FocusedEditTextItem {

    private final boolean focusable;

    private final boolean focusableInTouchMode;

    @NonNull
    private final EditText editText;

    private FocusedEditTextItem(final @NonNull EditText v) {
        editText = v;
        focusable = v.isFocusable();
        focusableInTouchMode = v.isFocusableInTouchMode();
    }

    private final void clearFocus() {
        if(focusable)
            editText.setFocusable(false);
        if(focusableInTouchMode)
            editText.setFocusableInTouchMode(false);
        editText.clearFocus();
    }

    private final void reset() {
        if(focusable)
            editText.setFocusable(true);
        if(focusableInTouchMode)
            editText.setFocusableInTouchMode(true);
    }

}

private final static class FocusedEditTextItems extends ArrayList<FocusedEditTextItem> {

    private final void addAndClearFocus(final @NonNull EditText v) {
        final FocusedEditTextItem item = new FocusedEditTextItem(v);
        add(item);
        item.clearFocus();
    }

    private final boolean containsView(final @NonNull View v) {
        boolean result = false;
        for(FocusedEditTextItem item: this) {
            if(item.editText == v) {
                result = true;
                break;
            }
        }
        return result;
    }

    private final void reset() {
        for(FocusedEditTextItem item: this)
            item.reset();
    }

}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.