Fragment nie jest zastępowany, ale nakładany na poprzedni

105

Czynność:

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

Fragment1 fragment = new Fragment1();
Fragment2 fragment2 = new Fragment2();

transaction.replace(R.id.Fragment1, fragment);
transaction.addToBackStack(null);
transaction.commit();

FragmentTransaction transaction2 = getSupportFragmentManager().beginTransaction();
transaction2.replace(R.id.Fragment1, fragment2);
transaction2.addToBackStack(null);
transaction2.commit();

Kod w widoku:

<fragment
    android:id="@+id/Fragment1"
    android:name="com.landa.fragment.Fragment1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_below="@+id/include1" /> 

Problem polega na tym, że treść tak naprawdę nie jest zastępowana - zostaje umieszczona na wierzchu (więc zachodzi na siebie).

Kiedy klikam wstecz, pierwszy fragment jest wyświetlany poprawnie (bez drugiego), ale początkowo oba są widoczne (chcę, aby był widoczny tylko ostatni).

Czego tu brakuje?

Narzędzie
źródło
W moim przypadku problem wynikał z tego, że nie korzystałem z odpowiedniego id widoku kontenera, czyli nie podawałem poprawnego containerViewId(w replacemetodzie).
nbro

Odpowiedzi:

145

Robisz tutaj dwie rzeczy źle:

  1. Nie można zastąpić fragmentu, który jest umieszczony statycznie w xmlpliku układu. Należy utworzyć kontener (np. A FrameLayout) w układzie, a następnie dodać fragment programowo za pomocą FragmentTransaction.

  2. FragmentTransaction.replaceoczekuje identyfikatora kontenera zawierającego fragment, a nie identyfikatora fragmentu jako pierwszego parametru. Więc powinieneś przekazać pierwszy argument jako identyfikator kontenera, do którego dodałeś pierwszy fragment.

Możesz skorzystać z tego linku, aby uzyskać więcej informacji.

Sapan Diwakar
źródło
17
Używanie FrameLayoutjako kontenera jest lepsze niż używanie LinearLayout.
Marcel Bro
3
Użycie pustego FrameLayout w Aktywności rozwiązało również mój problem
Subtle Fox
1
To działa. Ale kiedy uruchamiam aplikację i od razu wciskam przycisk Wstecz, kończę w stanie, w którym fragment jest pusty, zamiast wychodzić z aplikacji?
MasterScrat
@MasterScrat Pomiń addTobackStack(..)podczas wykonywania transakcji po raz pierwszy. Pozwoli to uniknąć umieszczania pustego kontenera FrameLayout na tylnym stosie. Nadal będziesz mógł wrócić do dodawanego fragmentu.
Alex Meuer
Oto świetny film, który pokazuje twoje wyjaśnienie. youtube.com/watch?v=EbcdMxAIr54
SQL and Java Learner
32

Miałem podobny problem, ale moim problemem było to, że korzystałem z dwóch różnych menedżerów fragmentów: jednego z getSupportFragmentManager()i jednego z getFragmentManager(). Gdybym dodał jeden fragment z, SupportFragmentManagera następnie spróbował zastąpić fragment z FragmentManager, fragment zostałby dodany na górze. Musiałem zmienić kod, FragmentManagerżeby był używany i to załatwiło sprawę.

swbandit
źródło
7

Miałem ten sam problem i widziałem wszystkie odpowiedzi, ale żadna z nich nie zawierała mojego błędu! Przed próbą zastąpienia obecnego fragmentu pokazywałem domyślny fragment w xml aktywności kontenera w następujący sposób:

<FrameLayout
    android:id="@+id/fragment_frame_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="....ShopFragment"
        android:id="@+id/fragment"
        tools:layout="@layout/fragment_shop" />
</FrameLayout>

po tym, chociaż przechodziłem FrameLayoutdo, fragmentTransaction.replace()ale miałem dokładny problem. pokazywał drugi fragment na poprzednim.

Problem rozwiązany poprzez usunięcie fragmentu z xml i pokazanie go programowo w onCreate()metodzie działania kontenera dla domyślnego podglądu na starcie programu w następujący sposób:

    fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.fragment_frame_layout,new ShopFragment());
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();

i kontener działań xml:

<FrameLayout
    android:id="@+id/fragment_frame_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
Emad
źródło
4

Przed zastąpieniem fragmentów możesz wyczyścić BackStack:

    FragmentManager fm = getActivity().getSupportFragmentManager();

    for (int i = 0; i < fm.getBackStackEntryCount(); i++) {
        fm.popBackStack();
    }

    // replace your fragments
Michał Z.
źródło
problem polega na tym, że zadeklarował on fragment w swoim pliku układu xml. Dlatego nie można go usunąć / wymienić w czasie wykonywania.
Poonam Anthony
to zadziałało dla mnie, próba zastąpienia fragmentu bez czyszczenia backstacka wydawała się pozostawić najnowszy wpis backstack nad jakimkolwiek innym fragmentem.
speedynomads
3
<FrameLayout
    android:id="@+id/fragment_frame_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?android:windowBackground">
//Add fragment here
</FrameLayout>

Dodaj android:background="?android:windowBackground">do pojemnika, w którym znajduje się Twój fragment. W moim przypadku dodałem FrameLayout, to powinno pomóc.

ralph tashinga nyoni
źródło
1

Użyj kontenera zamiast fragmentu

<FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Hello World!" />

Teraz w MainActivity

if(condition)
    getFragmentManager().beginTransaction().replace(R.id.container,new FirstFragment()).commit();                    
else
    getFragmentManager().beginTransaction().replace(R.id.container, new SecondFragment()).commit();

Zwróć uwagę, że Twoja klasa fragmentów powinna importować „android.app.Fragment”. Upewnij się, że używasz „support.v4” lub „oryginalnych” implementacji i nie mieszaj ich. Dostałem ten problem, ponieważ je wymieszałem.

Vikash Kumar Verma
źródło
0

Zgadzam się z odpowiedzią Sapana Diwakara. Ale znalazłem obejście, jak to zrobić.

Po pierwsze, w swojej aktywności (np. activity_main), W której masz wszystkie układy, dodaj plik FrameLayout. Powinna mieć wysokość i szerokość match parent. Nadaj mu również plik id.

Teraz w twoim fragmencie, który ma zastąpić obecny układ, wywołaj onPause()i onResume(). Zainicjuj wszystkie wewnętrzne pojemniki w formacie View. I ustaw ich visibilitydo GONEwewnątrzonResume() i do VISIBLEwewnątrzonPause() .

UWAGA : Nie ten, FrameLayoutktóry zastępujesz tym fragmentem.

Załóżmy, że masz ViewPager, TabLayout, ImageViewa zwyczaj <fragment>w activity_main.xml. Dodaj, FrameLayoutjak wspomniano powyżej.

W abcFragment.java, w onClick()zależności, dodać addToBackstack(null)w transaction.

Przykładowy kod:

w xyzFragment, który ma zastąpić abcFragment (i wszystko pokazane w activity_main.xml), zrób to

@Override
    public void onResume() {
        super.onResume();

    // On resume, make everything in activity_main invisible except this fragment.
    slidingTabs = getActivity().findViewById(R.id.sliding_tabs);
    viewPager = getActivity().findViewById(R.id.pager);
    miniPlayerFragment = getActivity().findViewById(R.id.mini_pager);

    slidingTabs.setVisibility(View.GONE);
    viewPager.setVisibility(View.GONE);
    miniPlayerFragment.setVisibility(View.GONE);
}

@Override
public void onPause() {
    super.onPause();

    // Make everything visible again
    slidingTabs = getActivity().findViewById(R.id.sliding_tabs);
    viewPager = getActivity().findViewById(R.id.pager);
    miniPlayerFragment = getActivity().findViewById(R.id.mini_pager);

    slidingTabs.setVisibility(View.VISIBLE);
    viewPager.setVisibility(View.VISIBLE);
    miniPlayerFragment.setVisibility(View.VISIBLE);
}

Miłego kodowania!

TheOnlyAnil
źródło
0

Możesz użyć metody setTransition z FragmentTransaction, aby naprawić nakładający się problem.

transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
Lavakush
źródło
0

W Oncreate ()

  BaseFragmentloginpage fragment = new BaseFragmentloginpage();
            android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.replace(R.id.frame, fragment);
            fragmentTransaction.isAddToBackStackAllowed();
            fragmentTransaction.commit();

teraz to będzie zawsze twój domyślny fragment ... jeśli dodasz fragment w układzie xml będzie on zawsze nakładał się, więc ustaw początkową zawartość z fragmentem w czasie wykonywania

Volverine
źródło
0

miałem ten sam problem po rozwiązaniu tego problemu

  1. Weź układ ramek, w którym zastępujesz fragment ur

    <FrameLayout
                    android:id="@+id/fragment_place"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
  2. i kod do wymiany

    public void selectFrag(View view) {
    Fragment fr;
    
      if(view == findViewById(R.id.button2))
    {
         fr = new FragmentTwo();
    
     }else {
         fr = new FragmentOne();
     }
     FragmentManager fm = getFragmentManager();
     FragmentTransaction fragmentTransaction = fm.beginTransaction();
     fragmentTransaction.replace(R.id.fragment_place, fr);
     fragmentTransaction.commit();

jeśli chcesz cały przykład, wyślę ci tylko komentarz ......

Sunil
źródło
0

Miałem też ten sam problem, ale to dlatego, że nie zmieniłem koloru tła fragmentu, który próbowałem dodać do mojego framelayout.

Spróbuj to zrobić za pomocą layout_heighti layout_widthustaw namatch_parent

Swastik Udupa
źródło
0

Kiedyś miałem ten problem i dowiedziałem się, że to dlatego, że przypadkowo usunąłem android:backgroundatrybut, którego brakuje w twoim kodzie xml. Uważam, że to działa tak, jak podczas malowania ściany, android:backgroundjest to kolor, który będzie miała ściana, a inne widoki są umieszczane na tym kolorze podstawowym. Jeśli nie pomalujesz ściany przed umieszczeniem widoków, będą one nad tym, co już było w tej ścianie, w twoim przypadku na drugim fragmencie. Nie wiem, czy to ci pomoże, powodzenia.

Miguel Pinheiro
źródło
0

Przede wszystkim próbowałem i próbowałem rozwiązać problem, ale nadal nie mam rozwiązania. Nie wiem, dlaczego to nie działa dla mnie. Zgadzam się z powyższymi odpowiedziami, ponieważ tak wielu się z nimi zgadza.

Po prostu przedłużam odpowiedź, a poniżej jest sposób, który działa dla mnie.

Dodałem tę właściwość android:clickable="true"w górnym układzie nadrzędnym w fragment.xmlpliku.

Mam nadzieję, że ktoś otrzyma pomoc.

Shailesh
źródło
0

Dziękuję Sapanowi Diwakarowi i innym. Ta odpowiedź mi pomogła. Po prostu utworzyłem RelativeLayout bez fragmentu w środku, użyłem RelativeLayout jako widoku nadrzędnego lub grupy, określiłem ten widok grupy w transakcji do zastąpienia i zadziałało.

Kod:

getSupportFragmentManager().beginTransaction()
                            .replace(R.id.content_group_view, itemDetailFragment).commit();

XML:

 <RelativeLayout
         android:id="@+id/content_group_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent">

 </RelativeLayout>
Craig D.
źródło
0

Sprawdź identyfikator układu ramki i wymień go jak poniżej

fragmentTransactionChange = MainActivity.this.getSupportFragmentManager().beginTransaction();
fragmentTransactionChange.replace(R.id.progileframe, changeFragment);
fragmentTransactionChange.addToBackStack(null);
fragmentTransactionChange.commit();
Sandeep Pareek
źródło