Fragment sur un autre problème de fragment

271

Lorsque je montre un fragment (qui est en plein écran avec un #77000000arrière-plan) sur un autre fragment (appelons-le principal), mon fragment principal réagit toujours aux clics (nous pouvons cliquer sur un bouton même si nous ne le voyons pas).

Question : comment empêcher les clics sur le premier fragment (principal)?

ÉDITER

Malheureusement, je ne peux pas simplement cacher le fragment principal, car j'utilise un arrière-plan transparent sur le deuxième fragment (afin que l'utilisateur puisse voir ce qui se trouve derrière).

Dmitry Zaytsev
la source
D' après ce que vous nous avez donné à travailler, vous devriez essayer de régler la Visibilityvotre main Fragmentà GONEquand vous ne l' utilisez pas.
adneal
1
Sans voir comment vous implémentez votre méthode onClicked, je suppose que vous retournez "false" lorsque vous cliquez dessus.
DeeV
@DeeV, la onClickméthode ne renvoie rien. Mais vous donnez une idée, merci (je posterai bientôt une réponse).
Dmitry Zaytsev
1
D'oh. Vous avez raison. onTouch le renvoie. Je souhaite juste avoir compris pourquoi un événement tactile est tombé à travers un fragment. Cela ne devrait pas le faire si vous n'émettez pas d'événements tactiles.
DeeV
@DeeV, on dirait que si votre vue (qui, par exemple au-dessus d'une autre) n'attrape pas l'événement onTouch, alors le système continue de rechercher d'autres vues avec les mêmes coordonnées.
Dmitry Zaytsev

Réponses:

578

Définissez la clickablepropriété true sur la vue du deuxième fragment. La vue capturera l'événement afin qu'il ne soit pas transmis au fragment principal. Donc, si la vue du deuxième fragment est une mise en page, ce serait le code:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true" />
Jure Vizjak
la source
4
Cela a fonctionné pour moi. Cela semble plus facile que la solution proposée par @Dmitry Zaitsev ci-dessus. Y a-t-il une raison pour laquelle ce serait une mauvaise idée? Je n'arrive pas à penser à tout, mais je veux juste être sûr.
ariets
1
Ça n'a pas marché pour moi. J'ai un RelativeLayoutà l'intérieur du fragment, et j'ai mis toute la vue avec la clickeablepropriété. La solution de @Dmitry résout mon problème.
4gus71n
3
Cela a fonctionné pour moi. "cliquable" dans Android est apparemment un peu comme "userInteractionEnabled" d'
iOS
17
Pourquoi Android nous soumet-il à des conditions de codage difficiles?!
Lo-Tan
1
J'ai le même problème avec ViewPager. Lorsque je défile sur la première page, cela passe également à la deuxième page, et cette solution n'a pas fonctionné pour moi. Des idées?
Gokhan Arik
72

La solution est assez simple. Dans notre deuxième fragment (qui chevauche notre fragment principal), nous avons juste besoin d'attraper l' onTouchévénement:

@Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstance){
    View root = somehowCreateView();

    /*here is an implementation*/

    root.setOnTouchListener(new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            return true;
        }
    });
    return root;
}
Dmitry Zaytsev
la source
votre solution a fonctionné +1 pour cela, mais pouvez-vous me dire pourquoi nous devons le faire explicitement?
Hunt
@Chassez-vous pas. C'est juste une autre façon de le faire (voir la réponse acceptée)
Dmitry Zaytsev
android: clickable = "true" sur la disposition principale xml de votre deuxième fragment.
Rishabh Srivastava
Il y a un problème avec cette solution, lorsque vous êtes en mode talkback d'accessibilité sur ON, elle ne lit pas les éléments individuels au lieu de cela se concentre sur la vue racine.
Amit Garg
Version de Kotlin: root.setOnTouchListener {_, _ -> true}
Gal Rom
21

Ajoutez simplement clickable="true"et focusable="true"à la disposition parent

 <android.support.constraint.ConstraintLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:clickable="true"
      android:focusable="true">

      <!--Your views-->

 </android.support.constraint.ConstraintLayout>

Si vous utilisez AndroidX, essayez ceci

 <androidx.constraintlayout.widget.ConstraintLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:clickable="true"
      android:focusable="true">

          <!--Your views-->

 </androidx.constraintlayout.widget.ConstraintLayout>
Miravzal
la source
Est-ce en quelque sorte différent de la réponse acceptée? focuseablen'est pas vraiment nécessaire.
Dmitry Zaytsev
2
Je pense que focusable="true"c'est juste pour éviter un avertissement dans Android Studio.
Artem M
9

Vous devez masquer le premier fragment lorsque vous affichez le deuxième fragment si deux fragments sont placés dans la même vue de conteneur.

Si vous souhaitez en savoir plus sur la façon de résoudre les problèmes de Fragment, vous pouvez voir ma bibliothèque: https://github.com/JustKiddingBaby/FragmentRigger

FirstFragment firstfragment;
SecondFragment secondFragment;
FragmentManager fm;
FragmentTransaction ft=fm.beginTransaction();
ft.hide(firstfragment);
ft.show(secondFragment);
ft.commit();
JingYeoh
la source
1
Cela devrait être la bonne réponse! Merci mec. J'ai résolu cette question sur un autre projet. Mais j'ai oublié comment je l'ai résolu et enfin, j'ai obtenu votre réponse. Je vous remercie.
Licat Julius
1
Je ne pense pas que ce soit la bonne solution. Les fragments / activités fonctionnent dans une pile de vues. Vous devrez appeler à nouveau .show après que le fragment supérieur soit sorti de la pile, ce qui signifie que le fragment inférieur doit être informé que celui du haut a disparu. C'est juste une logique supplémentaire à maintenir.
XY
4

Vous devez ajouter android:focusable="true"avecandroid:clickable="true"

Clickable signifie qu'il peut être cliqué par un périphérique pointeur ou être tapé par un périphérique tactile.

Focusablesignifie qu'il peut obtenir le focus d'un périphérique d'entrée comme un clavier. Les périphériques d'entrée tels que les claviers ne peuvent pas décider à quelle vue envoyer ses événements d'entrée en fonction des entrées elles-mêmes, ils les envoient donc à la vue qui a le focus.

Hossam Hassan
la source
2
et focusable = "true" est nécessaire pour éviter un avertissement dans le nouveau Android Studio
Hossam Hassan
2

Il y a plus d'une solution que certains d'entre nous ont contribué à ce fil, mais je voudrais également mentionner une autre solution. Si vous n'avez pas envie de mettre clickable et focusable égal à true pour le ViewGroup racine de chaque mise en page en XML comme moi. Vous pouvez également le mettre à votre base si vous en avez un comme ci-dessous;

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ) : View? {
        super.onCreateView(inflater, container, savedInstanceState)

        val rootView = inflater.inflate(layout, container, false).apply {
            isClickable = true
            isFocusable = true
        }

        return rootView
    }

Vous pouvez également utiliser la variable inline mais je ne l'ai pas préférée pour mes raisons.

J'espère que cela aide ceux qui détestent les XML de mise en page.

Yekta Sarıoğlu
la source
1

La réponse acceptable "fonctionnera", mais entraînera également un coût de performance (sur-dimensionnement, nouvelle mesure en cas de changement d'orientation), car le fragment en bas est toujours en cours d'élaboration. Peut-être que vous devriez simplement trouver le fragment par étiquette ou ID et définir la visibilité sur GONE ou VISIBLE lorsque vous devez à nouveau afficher.

À Kotlin:

fragmentManager.findFragmentByTag(BottomFragment.TAG).view.visibility = GONE

Cette solution est préférable à l'alternative hide()et aux show()méthodes FragmentTransactionlorsque vous utilisez des animations. Vous l'appelez simplement à partir du onTransitionStart()et onTransitionEnd()du Transition.TransitionListener.

Allan Veloso
la source
1

Metod 1:

Vous pouvez ajouter à la disposition de tous les fragments

android:clickable="true"
android:focusable="true"
android:background="@color/windowBackground"

Metod 2: (par programme)

Étendez tous les fragments de FragmentBaseetc. Ajoutez ensuite ce code àFragmentBase

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    getView().setBackgroundColor(getResources().getColor(R.color.windowBackground));
    getView().setClickable(true);
    getView().setFocusable(true);
}
MarsPeople
la source
0

Ce que vous pouvez faire, c'est que vous pouvez donner un clic vide à la disposition du fragment précédent en utilisant la propriété onClick pour la disposition parent de ce fragment principal et en activité, vous pouvez créer une fonction doNothing(View view)et n'y écrire rien. Cela le fera pour vous.

Satish Silveri
la source
0

Cela ressemble à un cas pour DialogFragment. Sinon, avec Fragment Manager, validez l'un pour masquer et l'autre pour afficher. Cela a fonctionné pour moi.

Juan Mendez
la source
0

L'ajout de android:clickable="true"n'a pas fonctionné pour moi. Cette solution ne fonctionne pas sur CoordinatorLayout lorsqu'il s'agit d'une disposition parent. C'est pourquoi j'ai fait RelativeLayout en tant que disposition parent, y ai ajouté android:clickable="true"et placé CoordinatorLayout sur ce RelativeLayout.

Zhebzhik Babich
la source
0

J'ai eu plusieurs fragments avec le même xml.
Après avoir passé des heures, j'ai supprimé setPageTransformeret ça a commencé à fonctionner

   //  viewpager.setPageTransformer(false, new BackgPageTransformer())

J'avais une logique effrayante.

public class BackgPageTransformer extends BaseTransformer {

    private static final float MIN_SCALE = 0.75f;

    @Override
    protected void onTransform(View view, float position) {
        //view.setScaleX Y
    }

    @Override
    protected boolean isPagingEnabled() {
        return true;
    }
}
AskQ
la source