Il frammento non viene sostituito ma viene aggiunto al precedente


104

Attività:

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();

Codice nella vista:

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

Il problema è che il contenuto non viene davvero sostituito: viene messo in cima (quindi si sovrappone).

Quando clicco indietro, il primo frammento viene mostrato correttamente (senza il secondo), ma inizialmente sono entrambi visibili (voglio che sia visibile solo l'ultimo).

Cosa mi manca qui?


Nel mio caso il problema era dovuto al fatto che non stavo usando l'id appropriato della visualizzazione del contenitore, cioè non stavo passando il corretto containerViewId(nel replacemetodo).
nbro

Risposte:


145

Stai facendo due cose sbagliate qui:

  1. Non è possibile sostituire un frammento posizionato staticamente in un xmlfile di layout. È necessario creare un contenitore (ad esempio a FrameLayout) nel layout e quindi aggiungere il frammento in modo programmatico utilizzando FragmentTransaction.

  2. FragmentTransaction.replacesi aspetta l'id del contenitore che contiene il frammento e non l'id del frammento come primo parametro. Quindi dovresti passare il primo argomento come id del contenitore a cui hai aggiunto il primo frammento.

Puoi fare riferimento a questo link per maggiori dettagli.


17
Usare FrameLayoutcome contenitore è meglio che usare LinearLayout.
Marcel Bro

3
L'uso di un FrameLayout vuoto in Activity ha risolto anche il mio problema
Subtle Fox

1
Funziona in questo modo. Ma poi quando avvio la mia applicazione e premo subito il tasto indietro, mi ritrovo in uno stato in cui il frammento è vuoto, invece di uscire dall'app?
MasterScrat

@MasterScrat Ometti addTobackStack(..)quando esegui la transazione per la prima volta. Ciò eviterà di posizionare il contenitore FrameLayout vuoto sullo stack posteriore. Sarai comunque in grado di tornare al frammento che hai aggiunto.
Alex Meuer

Ecco un ottimo video che dimostra la tua spiegazione. youtube.com/watch?v=EbcdMxAIr54
SQL and Java Learner

32

Ho avuto un problema simile, ma il mio problema era che stavo utilizzando due diversi gestori di frammenti: uno da getSupportFragmentManager()e uno da getFragmentManager(). Se aggiungessi un frammento con SupportFragmentManagere poi provassi a sostituire il frammento con FragmentManager, il frammento verrebbe semplicemente aggiunto sopra. Avevo bisogno di cambiare il codice in modo che FragmentManagervenisse utilizzato lo stesso e questo risolvesse il problema.


17

Il sito per sviluppatori Android suggerisce l'uso di a FrameLayoutper caricare dinamicamente i frammenti in fase di esecuzione. Hai hardcoded il frammento nel tuo file xml. Quindi non può essere rimosso in fase di esecuzione come menzionato in questo collegamento:

http://developer.android.com/training/basics/fragments/creating.html

questo link mostra come aggiungere frammenti tramite il tuo programma:

http://developer.android.com/training/basics/fragments/fragment-ui.html


7

Ho avuto lo stesso problema e ho visto tutte le risposte, ma nessuna di esse conteneva il mio errore! Prima di provare a sostituire il frammento corrente, stavo mostrando il frammento predefinito nell'xml dell'attività del contenitore in questo modo:

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

dopo di che, anche se stavo passando il FrameLayouta fragmentTransaction.replace()ma ho avuto il problema esatto. mostrava il secondo frammento sopra il precedente.

Il problema è stato risolto rimuovendo il frammento dall'xml e mostrandolo a livello di codice nel onCreate()metodo dell'attività del contenitore per l'anteprima predefinita all'avvio del programma in questo modo:

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

e l'attività del contenitore xml:

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

4

Puoi cancellare backStack prima di sostituire i frammenti:

    FragmentManager fm = getActivity().getSupportFragmentManager();

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

    // replace your fragments

il problema è che ha dichiarato il frammento nel suo file di layout xml. Quindi non può essere rimosso / sostituito in fase di esecuzione.
Poonam Anthony

questo ha funzionato per me, cercando di sostituire un frammento senza cancellare il backstack sembrava lasciare la voce backstack più recente sopra qualsiasi altro frammento.
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>

Aggiungi android:background="?android:windowBackground">al contenitore in cui risiede il tuo frammento. Nel mio caso ho aggiunto un FrameLayout, che dovrebbe aiutare.


1

Usa un contenitore invece di usare il frammento

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

Ora in MainActivity

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

Nota che la tua classe fragment dovrebbe importare 'android.app.Fragment' Assicurati di utilizzare "support.v4" o le implementazioni "originali" e non mischiarle. Stavo riscontrando questo problema perché li avevo mescolati.


0

Sono d'accordo con la risposta di Sapan Diwakar. Ma ho trovato una soluzione alternativa su come farlo.

Innanzitutto, nella tua attività (ad esempio activity_main), dove hai tutti i layout, aggiungi un file FrameLayout. L'altezza e la larghezza dovrebbero essere match parent. Inoltre, dagli un id.

Ora, nel tuo frammento che sostituirà il layout corrente, chiama onPause()e onResume(). Inizializza tutti i contenitori interni in View. E impostare il loro visibilitya GONEinonResume() e VISIBLEinonPause() .

NOTA : non quello FrameLayoutche stai sostituendo con questo frammento.

Supponiamo di avere ViewPager, TabLayout, ImageViewe un costume <fragment>in activity_main.xml. Aggiungi, FrameLayoutcome menzionato sopra.

In abcFragment.java, in onClick()funzione, aggiungere addToBackstack(null)in transaction.

Codice di esempio:

in xyzFragment, che sostituirà abcFragment (e tutto ciò che viene mostrato in activity_main.xml), fallo

@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);
}

Buona programmazione!


0

È possibile utilizzare il metodo setTransition da FragmentTransaction per risolvere il problema di sovrapposizione.

transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

0

In Oncreate ()

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

ora questo sarà sempre il tuo frammento predefinito ... se aggiungi un frammento nel layout xml si sovrapporrà sempre, quindi imposta il contenuto iniziale con frammento in fase di esecuzione


0

ho avuto lo stesso problema dopo aver usato questo problema risolto

  1. Prendi il layout del frame in cui stai sostituendo il tuo frammento

    <FrameLayout
                    android:id="@+id/fragment_place"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
  2. e codice per la sostituzione

    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();

se vuoi un esempio intero ti mando solo commentami ...


0

Ho anche avuto lo stesso problema, ma era perché non avevo cambiato il colore di sfondo del frammento che stavo cercando di aggiungere al mio framelayout.

Prova a farlo, con layout_heighte layout_widthimpostato sumatch_parent


0

Una volta ho avuto questo problema e ho scoperto che era perché avevo cancellato accidentalmente l' android:backgroundattributo che mancava nel tuo codice xml. Credo che funzioni come quando dipingi un muro, android:backgroundè il colore che sarà il muro e le altre viste sono posizionate sopra quel colore di base. Quando non dipingi il muro prima di posizionare le tue viste, saranno sopra ciò che era già in quel muro, nel tuo caso, l'altro tuo frammento. Non so se questo ti aiuterà, buona fortuna.


0

Ho provato soprattutto il caso e provo a risolvere il problema ma non ho ancora ottenuto la soluzione. Non so perché non funziona per me. Sono d'accordo con le risposte di cui sopra perché così tanti sono d'accordo.

Sto solo estendendo la risposta e di seguito è il modo in cui funziona per me.

Ho aggiunto questa proprietà android:clickable="true"nel layout principale superiore su fragment.xmlfile.

Spero che qualcuno riceva aiuto.


0

Grazie a Sapan Diwakar e ad altri. Questa risposta mi ha aiutato. Ho semplicemente creato un RelativeLayout senza un frammento al suo interno, ho usato RelativeLayout come vista principale o di gruppo, ho specificato quella vista di gruppo nella transazione da sostituire e ha funzionato.

Codice:

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>

0

Controlla l'ID del layout della cornice e sostituiscilo come di seguito

fragmentTransactionChange = MainActivity.this.getSupportFragmentManager().beginTransaction();
fragmentTransactionChange.replace(R.id.progileframe, changeFragment);
fragmentTransactionChange.addToBackStack(null);
fragmentTransactionChange.commit();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.