web-dev-qa-db-fra.com

Android fait disparaître la vue en cliquant en dehors de celle-ci

J'ai certains points de vue que je rend visibles lors d'une pression sur un bouton. Je veux qu'ils disparaissent si je clique en dehors de ces vues.

Comment cela serait-il fait sur Android? 

En outre, je me rends compte que le «bouton précédent» peut également aider les utilisateurs d’Android à utiliser cette fonction (je pourrais l’utiliser comme moyen secondaire de fermer les vues), mais certaines tablettes n’utilisent même plus de bouton «physique», a été très insuffisant.

29
CQM

Un moyen facile/stupide:

  • Créez une vue vide factice (disons ImageView sans source), faites-la remplir parent

  • Si vous cliquez dessus, faites ce que vous voulez faire.

La balise racine de votre fichier XML doit être un RelativeLayout. Il contiendra deux éléments: votre vue fictive (définissez sa position sur align the Parent Top). L'autre est votre vue d'origine contenant les vues et le bouton (cette vue peut être un LinearLayout ou quoi que vous fassiez. N'oubliez pas de définir sa position sur align the Parent Top)

J'espère que cela vous aidera, bonne chance!

31
iTurki

C'est une vieille question, mais je pensais donner une réponse qui ne serait pas basée sur des événements onTouch. Comme suggéré par RedLeader, il est également possible d'y parvenir en utilisant des événements focus. Dans certains cas, je devais afficher et masquer un ensemble de boutons disposés dans une fenêtre contextuelle personnalisée, c’est-à-dire que tous les boutons étaient placés dans la même ViewGroup. Certaines choses que vous devez faire pour que cela fonctionne:

  1. Le groupe de vues que vous souhaitez masquer doit avoir View.setFocusableInTouchMode(true) défini. Ceci peut également être défini en XML à l'aide de Android:focusableintouchmode.

  2. Votre racine de vue, c’est-à-dire la racine de toute votre mise en page, probablement une sorte de mise en page linéaire ou relative, doit également pouvoir être mise au point conformément au n ° 1 ci-dessus.

  3. Lorsque le groupe de vues est affiché, appelez View.requestFocus() pour lui donner le focus.

  4. Votre groupe de vues doit soit remplacer View.onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect), soit implémenter votre propre OnFocusChangeListener et utiliser View.setOnFocusChangeListener().

  5. Lorsque l'utilisateur appuie en dehors de votre vue, le focus est transféré vers la racine de la vue (puisque vous l'avez définie comme focusable dans # 2) ou vers une autre vue qui est intrinsèquement focusable (EditText ou similaire)

  6. Lorsque vous détectez une perte de la mise au point à l’aide d’une des méthodes décrites au point 4, vous savez que la mise au point a été transférée vers quelque chose qui ne fait pas partie de votre groupe de vues et vous pouvez la masquer.

Je suppose que cette solution ne fonctionne pas dans tous les scénarios, mais cela a fonctionné dans mon cas spécifique et il semble que cela puisse également fonctionner pour le PO.

23
britzl

Recherchez le rectangle de vue, puis déterminez si l'événement click est en dehors de la vue.

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Rect viewRect = new Rect();
    mTooltip.getGlobalVisibleRect(viewRect);
    if (!viewRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
        setVisibility(View.GONE);
    }
    return true;
}

Si vous souhaitez utiliser l'événement tactile à un autre endroit, essayez

return super.dispatchTouchEvent(ev);
21
Kai Wang

Je cherchais un moyen de fermer ma vue lorsque je touchais l'extérieur et aucune de ces méthodes ne répondait vraiment à mes besoins. J'ai trouvé une solution et je la posterai ici au cas où quelqu'un serait intéressé.

J'ai une activité de base qui s'étend à peu près toutes mes activités. Dans cela j'ai:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (myViewIsVisible()){
            closeMyView();
        return true;
    }
    return super.dispatchTouchEvent(ev);
}

Donc, si ma vue est visible, elle se fermera et sinon, elle se comportera comme un événement tactile normal. Je ne sais pas si c'est la meilleure façon de le faire, mais cela semble fonctionner pour moi.

10
pickle_inspector

J'avais besoin de la capacité spécifique non seulement pour supprimer une vue lorsque vous cliquez en dehors de celle-ci, mais également pour permettre au clic de passer normalement à l'activité. Par exemple, j’ai une mise en page distincte, notification_bar.xml, que j’ai besoin de gonfler et d’ajouter de manière dynamique à l’activité en cours, le cas échéant.

Si je crée une vue superposée de la taille de l'écran pour recevoir des clics en dehors de la vue notification_bar et supprimer ces deux vues d'un clic, la vue parent (la vue principale de l'activité) n'a toujours pas reçu de clics, ce qui signifie lorsque la notification_bar est visible, il suffit de deux clics pour cliquer sur un bouton (un pour fermer la vue notification_bar et un pour cliquer sur le bouton).

Pour résoudre ce problème, vous pouvez simplement créer votre propre groupe DismissViewGroup qui étend ViewGroup et remplace la méthode suivante:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    ViewParent parent = getParent();
    if(parent != null && parent instanceof ViewGroup) {
        ((ViewGroup) parent).removeView(this);
    }
    return super.onInterceptTouchEvent(ev);
}

Et puis votre vue dynamiquement ajoutée ressemblera un peu à:

<com.example.DismissViewGroup Android:id="@+id/touch_interceptor_view"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:background="@Android:color/transparent" ...
    <LinearLayout Android:id="@+id/notification_bar_view" ...

Cela vous permettra d'interagir avec la vue. Dès que vous cliquez en dehors de la vue, vous fermez la vue et interagissez normalement avec l'activité.

2
byebrianwong

Implémentez onTouchListener(). Vérifiez que les coordonnées du contact sont en dehors des coordonnées de votre vue.

Il y a probablement un moyen de le faire avec onFocus(), etc. - Mais je ne le sais pas.

0
RedLeader

base sur Kai Wang réponse: je suggère d’abord de vérifier la visibilité de Votre vue, base sur mon scénario lorsque l’utilisateur clique sur fab myView devient visible, puis lorsque l’utilisateur clique en dehors de myView disparaît 

  @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Rect viewRect = new Rect();
    myView.getGlobalVisibleRect(viewRect);
    if (myView.getVisibility() == View.VISIBLE && !viewRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
        goneAnim(myView);
        return true;
    }
    return super.dispatchTouchEvent(ev);
}
0
FxRi4

J'ai créé ViewGroup personnalisé pour afficher une boîte d'informations ancrée dans une autre vue (bulle contextuelle) .

public BalloonView(View anchor, View child) {
    super(anchor.getContext());
    //calculate popup position relative to anchor and do stuff
    init(...);
    //receive child via constructor, or inflate/create default one
    this.child = child;
    //this.child = inflate(...);
    //this.child = new SomeView(anchor.getContext());
    addView(child);
    //this way I don't need to create intermediate ViewGroup to hold my View
    //but it is fullscreen (good for dialogs and absolute positioning)
    //if you need relative positioning, see @iturki answer above 
    ((ViewGroup) anchor.getRootView()).addView(this);
}

private void dismiss() {
    ((ViewGroup) getParent()).removeView(this);
}

Gérer les clics à l'intérieur de l'enfant:

child.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //write your code here to handle clicks inside
    }
});

Pour rejeter ma vue par clic extérieur SANS délégation de contact à la vue sous-jacente:

BalloonView.this.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        dismiss();
    }
});

Pour rejeter ma vue par un clic extérieur AVEC une délégation de contact à la vue sous-jacente:

@Override
public boolean onTouchEvent(MotionEvent event) {
    dismiss();
    return false; //allows underlying View to handle touch
}

Pour ignorer le bouton Retour enfoncé:

//do this in constructor to be able to intercept key
setFocusableInTouchMode(true);
requestFocus();

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        dismiss();
        return true;
    }
    return super.onKeyPreIme(keyCode, event);
}
0
Levinthann