Fragment über ein anderes Fragmentproblem

271

Wenn ich ein Fragment (Vollbild mit #77000000Hintergrund) über einem anderen Fragment zeige (nennen wir es Hauptfragment), reagiert mein Hauptfragment immer noch auf Klicks (wir können auf eine Schaltfläche klicken, auch wenn wir es nicht sehen).

Frage : Wie kann man Klicks auf das erste (Haupt-) Fragment verhindern?

BEARBEITEN

Leider kann ich das Hauptfragment nicht einfach ausblenden, da ich für das zweite Fragment einen transparenten Hintergrund verwende (damit der Benutzer sehen kann, was sich dahinter befindet).

Dmitry Zaytsev
quelle
Basierend auf dem, was Sie uns zur Arbeit gegeben haben, sollten Sie versuchen, die Einstellung Visibilityauf festzulegen main Fragment, GONEwenn Sie sie nicht verwenden.
Adneal
1
Ohne zu sehen, wie Sie Ihre onClicked-Methode implementieren, geben Sie vermutlich "false" zurück, wenn Sie darauf klicken.
DeeV
@DeeV, onClickMethode gibt nichts zurück. Aber du gibst eine Idee, danke (ich werde bald eine Antwort posten).
Dmitry Zaytsev
1
D'oh. Du hast recht. onTouch gibt es zurück. Ich wünschte nur, ich hätte verstanden, warum ein Berührungsereignis durch ein Fragment fiel. Dies sollte nicht der Fall sein, wenn Sie keine Berührungsereignisse ausgeben.
DeeV
@DeeV: Wenn Ihre Ansicht (die beispielsweise über der anderen angezeigt wird) das Ereignis onTouch nicht erfasst, sucht das System weiterhin nach anderen Ansichten mit denselben Koordinaten.
Dmitry Zaytsev

Antworten:

576

Setzen Sie die clickableEigenschaft in der Ansicht des zweiten Fragments auf true. Die Ansicht fängt das Ereignis ab, sodass es nicht an das Hauptfragment übergeben wird. Wenn also die Ansicht des zweiten Fragments ein Layout ist, wäre dies der 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
quelle
4
Das hat bei mir funktioniert. Es scheint einfacher zu sein als die Lösung, die @Dmitry Zaitsev oben gegeben hat. Gibt es einen Grund, warum dies eine schlechte Idee wäre? Ich kann anscheinend an nichts denken, aber ich möchte nur sicher sein.
Ariets
1
Das hat bei mir nicht funktioniert. Ich habe ein RelativeLayoutinnerhalb des Fragments und ich setze die gesamte Ansicht mit der clickeableEigenschaft. Die Lösung von @Dmitry löst mein Problem.
4gus71n
3
Das hat bei mir funktioniert. "clickable" in Android ist anscheinend etwas wie "userInteractionEnabled" von iOS
mvds
17
Warum unterwirft uns Android harten Codierungsbedingungen?!
Lo-Tan
1
Ich habe das gleiche Problem mit ViewPager. Wenn ich auf der ersten Seite scrolle, wird auch die zweite Seite angezeigt, und diese Lösung hat bei mir nicht funktioniert. Irgendwelche Ideen?
Gokhan Arik
72

Die Lösung ist ziemlich einfach. In unserem zweiten Fragment (das unser Hauptfragment überlappt) müssen wir nur das onTouchEreignis abfangen :

@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
quelle
Ihre Lösung hat dafür +1 geklappt, aber können Sie mir sagen, warum wir dies explizit tun müssen?
Hunt
@ Hunt musst du nicht. Das ist nur eine andere Möglichkeit (siehe akzeptierte Antwort)
Dmitry Zaytsev
android: clickable = "true" im XML-Hauptlayout Ihres zweiten Fragments.
Rishabh Srivastava
Bei dieser Lösung tritt ein Problem auf. Wenn Sie den Eingabehilfen für Eingabehilfen aktivieren, werden die einzelnen Elemente nicht gelesen, sondern der Fokus wird auf die Stammansicht gelegt.
Amit Garg
Kotlin-Version: root.setOnTouchListener {_, _ -> true}
Gal Rom
21

Fügen Sie einfach clickable="true"und focusable="true"zum übergeordneten Layout hinzu

 <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>

Wenn Sie verwenden AndroidX, versuchen Sie dies

 <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
quelle
Unterscheidet sich das irgendwie von der akzeptierten Antwort? focuseableist nicht wirklich notwendig.
Dmitry Zaytsev
2
Ich denke focusable="true"hier ist nur, um eine Warnung in Android Studio zu vermeiden.
Artem M
9

Sie sollten das erste Fragment ausblenden, wenn Sie das zweite Fragment anzeigen, wenn zwei Fragmente in derselben Containeransicht platziert sind.

Wenn Sie weitere Fragen zur Lösung von Fragmentproblemen haben möchten, finden Sie meine Bibliothek: https://github.com/JustKiddingBaby/FragmentRigger

FirstFragment firstfragment;
SecondFragment secondFragment;
FragmentManager fm;
FragmentTransaction ft=fm.beginTransaction();
ft.hide(firstfragment);
ft.show(secondFragment);
ft.commit();
JingYeoh
quelle
1
Dies sollte die richtige Antwort sein! Danke mann. Ich habe diese Frage in einem anderen Projekt gelöst. Aber ich habe vergessen, wie ich es gelöst habe und schließlich habe ich Ihre Antwort bekommen. Danke dir.
Licat Julius
1
Ich denke nicht, dass dies die richtige Lösung ist. Fragmente / Aktivitäten arbeiten in einem Ansichtsstapel. Sie müssen .show erneut aufrufen, nachdem das obere Fragment vom Stapel genommen wurde. Dies bedeutet, dass das untere Fragment darüber informiert werden muss, dass das obere Fragment verschwunden ist. Es ist nur zusätzliche Logik hinzugefügt, um zu pflegen.
XY
4

Sie müssen hinzufügen android:focusable="true"mitandroid:clickable="true"

Clickable bedeutet, dass es von einem Zeigergerät angeklickt oder von einem Touch-Gerät getippt werden kann.

Focusablebedeutet, dass es den Fokus von einem Eingabegerät wie einer Tastatur erhalten kann. Eingabegeräte wie Tastaturen können anhand der Eingaben selbst nicht entscheiden, an welche Ansicht ihre Eingabeereignisse gesendet werden sollen. Sie senden sie daher an die Ansicht mit Fokus.

Hossam Hassan
quelle
2
und focusable = "true" ist erforderlich, um eine Warnung im neuen Android Studio
Hossam Hassan
2

Es gibt mehr als eine Lösung, die einige von uns zu diesem Thread beigetragen haben, aber ich möchte auch eine andere Lösung erwähnen. Wenn Sie keine Lust haben, klickbar und fokussierbar zu setzen, entspricht dies der Root-ViewGroup jedes Layouts in XML wie mir. Sie können es auch in Ihre Basis stellen, wenn Sie eines wie unten haben.

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
    }

Sie können auch eine Inline-Variable verwenden, die ich jedoch aus meinen Gründen nicht bevorzugt habe.

Ich hoffe, es hilft für diejenigen, die Layout-XMLs hassen.

Yekta Sarıoğlu
quelle
1

Die akzeptable Antwort "funktioniert", verursacht aber auch Leistungskosten (Überziehen, erneutes Messen bei Orientierungsänderung), da das Fragment unten noch gezeichnet wird. Vielleicht sollten Sie das Fragment einfach anhand des Tags oder der ID finden und die Sichtbarkeit auf GONE oder VISIBLE setzen, wenn Sie es erneut anzeigen müssen.

In Kotlin:

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

Diese Lösung ist der Alternative hide()und den show()Methoden der FragmentTransactionVerwendung von Animationen vorzuziehen . Sie nennen es einfach aus dem onTransitionStart()und onTransitionEnd()von Transition.TransitionListener.

Allan Veloso
quelle
1

Metod 1:

Sie können allen Fragmenten ein Layout hinzufügen

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

Metod 2: (programmatisch)

Erweitern Sie alle Fragmente von FragmentBaseusw. Fügen Sie dann diesen Code hinzuFragmentBase

@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
quelle
0

Was Sie tun können, ist, dass Sie einen leeren Klick auf das Layout des vorherigen Fragments geben können, indem Sie die onClick-Eigenschaft zum übergeordneten Layout dieses Hauptfragments verwenden. In der Aktivität können Sie eine Funktion erstellen doNothing(View view)und nichts darin schreiben. Dies wird es für Sie tun.

Satish Silveri
quelle
0

Dies klingt nach einem Fall für DialogFragment. Andernfalls verpflichten Sie sich mit Fragment Manager, einen auszublenden und den anderen anzuzeigen. Das hat bei mir funktioniert.

Juan Mendez
quelle
0

Das Hinzufügen von android:clickable="true"hat bei mir nicht funktioniert. Diese Lösung funktioniert nicht mit CoordinatorLayout, wenn es sich um ein übergeordnetes Layout handelt. Aus diesem Grund habe ich RelativeLayout als übergeordnetes Layout erstellt, hinzugefügt android:clickable="true"und CoordinatorLayout in dieses RelativeLayout eingefügt .

Zhebzhik Babich
quelle
0

Ich hatte mehrere Fragmente mit derselben XML.
Nachdem ich Stunden verbracht hatte, entfernte ich mich setPageTransformerund es fing an zu funktionieren

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

Ich hatte eine schreckliche Logik.

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
quelle