2011-12-12 13 views
101

Utilizzo del pacchetto di compatibilità per il target 2.2 utilizzando Fragments.Ciclo di vita di un frammento Android su modifiche di orientamento

Dopo la ricodifica di un'attività per utilizzare i frammenti in un'app non sono riuscito a far funzionare le modifiche di orientamento/gestione dello stato, quindi ho creato una piccola app di test con una FragmentActivity singola e un frammento singolo.

I registri dalle modifiche di orientamento sono strani, con più chiamate ai frammenti OnCreateView.

Sono ovviamente manca qualcosa - come detatching il frammento e ricollegandolo piuttosto che creare una nuova istanza, ma non riesco a vedere alcuna documentazione che indicherebbe dove sto andando male.

Qualcuno può fare luce su quello che sto facendo male qui per favore. Grazie

Il registro è il seguente dopo le modifiche di orientamento.

Initial creation 
12-04 11:57:15.808: D/FragmentTest.FragmentTestActivity(3143): onCreate 
12-04 11:57:15.945: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:57:16.081: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null 


Orientation Change 1 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): onSaveInstanceState 
12-04 11:57:39.031: D/FragmentTest.FragmentTestActivity(3143): onCreate 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:57:39.167: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null 


Orientation Change 2 
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState 
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState 
12-04 11:58:32.361: D/FragmentTest.FragmentTestActivity(3143): onCreate 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null 
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null 

Main Activity (FragmentActivity)

public class FragmentTestActivity extends FragmentActivity { 
/** Called when the activity is first created. */ 

private static final String TAG = "FragmentTest.FragmentTestActivity"; 


FragmentManager mFragmentManager; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    Log.d(TAG, "onCreate"); 

    mFragmentManager = getSupportFragmentManager(); 
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 

    FragmentOne fragment = new FragmentOne(); 

    fragmentTransaction.add(R.id.fragment_container, fragment); 
    fragmentTransaction.commit(); 
} 

E il frammento

public class FragmentOne extends Fragment { 

private static final String TAG = "FragmentTest.FragmentOne"; 

EditText mEditText; 

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
     Bundle savedInstanceState) { 

    Log.d(TAG, "OnCreateView"); 

    View v = inflater.inflate(R.layout.fragmentonelayout, container, false); 

    // Retrieve the text editor, and restore the last saved state if needed. 
    mEditText = (EditText)v.findViewById(R.id.editText1); 

    if (savedInstanceState != null) { 

     Log.d(TAG, "OnCreateView->SavedInstanceState not null"); 

     mEditText.setText(savedInstanceState.getCharSequence("text")); 
    } 
    else { 
     Log.d(TAG,"OnCreateView->SavedInstanceState null"); 
    } 
    return v; 
} 

@Override 
public void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 

    Log.d(TAG, "FragmentOne.onSaveInstanceState"); 

    // Remember the current text, to restore if we later restart. 
    outState.putCharSequence("text", mEditText.getText()); 
} 

manifesto

<uses-sdk android:minSdkVersion="8" /> 

<application 
    android:icon="@drawable/ic_launcher" 
    android:label="@string/app_name" > 
    <activity 
     android:label="@string/app_name" 
     android:name=".activities.FragmentTestActivity" 
     android:configChanges="orientation"> 
     <intent-filter > 
      <action android:name="android.intent.action.MAIN" /> 

      <category android:name="android.intent.category.LAUNCHER" /> 
     </intent-filter> 
    </activity> 
</application> 
+0

Non so se è una risposta corretta, ma prova a utilizzare un tag quando aggiungi il frammento, aggiungi (R.id.fragment_container, fragment, "MYTAG") o, in caso contrario, sostituisci (R.id.fragment_container, frammento , "MYTAG") – Jason

+2

Fare alcune indagini. Quando l'attività principale (FragmentTestActivity) si riavvia al cambio di orientamento e ottengo una nuova istanza di FragmentManager, quindi eseguo un FindFragmentByTag per individuare il frammento che esiste ancora, in modo che il frammento venga mantenuto durante la ricreazione dell'attività principale. Se trovo il frammento e non faccio nulla, viene comunque nuovamente visualizzato con MainActivity. – MartinS

risposta

165

Stai stratificazione tuoi Frammenti uno sopra l'altro.

Quando si verifica un cambiamento di configurazione del vecchio frammento si aggiunge alla nuova attività quando è ricreato. Questo è un enorme dolore nella parte posteriore della maggior parte del tempo.

È possibile interrompere gli errori che si verificano utilizzando lo stesso frammento piuttosto che ricreare una nuova. È sufficiente aggiungere questo codice:

if (savedInstanceState == null) { 
    // only create fragment if activity is started for the first time 
    mFragmentManager = getSupportFragmentManager(); 
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 

    FragmentOne fragment = new FragmentOne(); 

    fragmentTransaction.add(R.id.fragment_container, fragment); 
    fragmentTransaction.commit(); 
} else {   
    // do nothing - fragment is recreated automatically 
} 

Attenzione però: i problemi si verificheranno se si tenta l'accesso e attività vista da dentro il frammento come i cicli di vita viene cambiato leggermente. (Ottenere le viste da un genitore Attività da un frammento non è facile).

+36

"Questo è un enorme dolore nella parte posteriore del tempo" (con il pollice in alto) – rushinge

+1

Come può essere gestito lo stesso scenario in caso di utilizzo di ViewPage con FragmentStatePagerAdapter ... qualche suggerimento? – CoDe

+4

Esiste un'affermazione simile nella documentazione ufficiale?Non è una contraddizione con quanto affermato nella guida: "Quando l'attività viene distrutta, lo sono anche tutti i frammenti"? Poiché "" quando l'orientamento dello schermo cambia, il sistema distrugge e ricrea l'attività [...] "'. – cYrus

5

È possibile @Override FragmentActivity utilizzando onSaveInstanceState(). Assicurati di non chiamare lo super.onSaveInstanceState() nel metodo.

+1

Questo probabilmente spezzerebbe il ciclo di vita delle attività introducendo più potenziali problemi in questo processo già piuttosto confuso. Cerca nel codice sorgente di FragmentActivity: salva gli stati di tutti i frammenti. – Brian

+0

Ho avuto il problema di avere un numero di schede diverso per un diverso orientamento. Quindi ho sempre avuto una strana situazione dopo aver acceso il dispositivo e aver fatto scorrere alcune pagine, ho ottenuto quello vecchio e sbagliato. con il girare di savedInstance funziona meglio senza perdite di memoria (ho usato setSavedEnabled (false) prima e ho finito con grandi perdite di memoria ad ogni cambio di orientamento) – Informatic0re

0

Dovremmo sempre provare a prevenire l'eccezione nullpointer, quindi dobbiamo controllare prima il metodo saveinstance per le informazioni sul bundle. per breve spiegazione per verificare questo blog link

public static class DetailsActivity extends Activity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     if (getResources().getConfiguration().orientation 
      == Configuration.ORIENTATION_LANDSCAPE) { 
      // If the screen is now in landscape mode, we can show the 
      // dialog in-line with the list so we don't need this activity. 
      finish(); 
      return; 
     } 

     if (savedInstanceState == null) { 
      // During initial setup, plug in the details fragment. 
      DetailsFragment details = new DetailsFragment(); 
      details.setArguments(getIntent().getExtras()); 
      getFragmentManager().beginTransaction().add(android.R.id.content, details).commit(); 
     } 
    } 
} 
78

Per citare this book, "per garantire un'esperienza utente coerente , Android persiste il layout Frammento ed associato indietro stack quando un attività viene riavviato a causa di una modifica alla configurazione." (P.124)

E il modo di approccio che è quello di controllare prima se lo stack Frammento di nuovo è già stato popolato, e creare la nuova istanza frammento solo se non ha:

@Override 
public void onCreate(Bundle savedInstanceState) { 

     ...  

    FragmentOne fragment = (FragmentOne) mFragmentManager.findFragmentById(R.id.fragment_container); 

    if (fragment == null) { 
     FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 
     fragmentTransaction.add(R.id.fragment_container, new FragmentOne()); 
     fragmentTransaction.commit(); 
    } 
} 
+2

Probabilmente mi hai salvato un sacco di tempo con questo ... grazie mille. Puoi combinare questa risposta con quella di Graeme per ottenere una soluzione perfetta per gestire modifiche e frammenti di configurazione. – azpublic

+10

Questa è in realtà la risposta giusta, non quella contrassegnata. Grazie mille! –

+0

come gestire lo stesso scenario in caso di implementazione del frammento di ViewPager. – CoDe

7

L'onCreate() il metodo della tua attività è chiamato dopo il cambiamento di orientamento come hai visto. Quindi, non eseguire FragmentTransaction che aggiunge il frammento dopo il cambio di orientamento nella tua attività.

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    if (savedInstanceState==null) { 
     //do your stuff 
    } 
} 

I frammenti devono e devono essere invariati.

0

Se basta fare un progetto, allora il project manager dice che è necessario per raggiungere schermo funzione di commutazione, ma non si vuole per lo screening di commutazione del carico layout diverso (in grado di creare layout e sistema di layout-port.

Determinerai automaticamente lo stato della schermata, caricherai il layout corrispondente), a causa della necessità di re inizializzare l'attività o il frammento, l'esperienza dell'utente non è buona, non direttamente sullo switch dello schermo, mi riferisco a ? Url = YgNfP-VHY-Nuldi7YHTfNet3AtLdN-w__O3z1wLOnzr3wDjYo7X7PYdNyhw8R24ZE22xiKnydni7R0r35s2fOLcHOiLGYT9Qh_fjqtytJki & wd = & eqid = f258719e0001f24000000004585a1082

La premessa è che il layout utilizzando il peso del modo in cui il layout del layout_weight, come segue:

<LinearLayout 
Android:id= "@+id/toplayout" 
Android:layout_width= "match_parent" 
Android:layout_height= "match_parent" 
Android:layout_weight= "2" 
Android:orientation= "horizontal" > 

Quindi il mio L'approccio è, quando si cambia schermo, non è necessario caricare un nuovo layout del file di visualizzazione, modificare il layout in pesi dinamici onConfigurationChanged, i seguenti passi: 1 primo set: AndroidManifest.xml nell'attributo activity: android: configChanges = "keyboardHidden | orientation | screenSize" Per impedire la commutazione dello schermo, evitare il re-caricamento, in modo da poter monitorare in onConfigurationChanged 2 attività di riscrittura o frammento nel metodo onConfigurationChanged.

@Override 
Public void onConfigurationChanged (Configuration newConfig) { 
    Super.onConfigurationChanged (newConfig); 
    SetContentView (R.layout.activity_main); 
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { 
     //On the layout// weight adjustment 
     LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout); 
     LinearLayout.LayoutParams LP = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f); 
     Toplayout.setLayoutParams (LP); 
     LinearLayout tradespace_layout = (LinearLayout) findViewById(R.id.tradespace_layout); 
     LinearLayout.LayoutParams LP3 = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f); 
     Tradespace_layout.setLayoutParams (LP3); 
    } 
    else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) 
    { 
     //On the layout// weight adjustment 
     LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout); 
     LinearLayout.LayoutParams LP = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f); 
     Toplayout.setLayoutParams (LP); 
     LinearLayout tradespace_layout = (LinearLayout) findViewById (R.id.tradespace_layout); 
     LinearLayout.LayoutParams LP3 = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f); 
     Tradespace_layout.setLayoutParams (LP3); 
    } 
} 
0

In caso di modifica della configurazione, il framework crea una nuova istanza del frammento e lo aggiunge all'attività. Così, invece di questo:

FragmentOne fragment = new FragmentOne(); 

fragmentTransaction.add(R.id.fragment_container, fragment); 

fare questo:

if (mFragmentManager.findFragmentByTag(FRAG1_TAG) == null) { 
    FragmentOne fragment = new FragmentOne(); 

    fragmentTransaction.add(R.id.fragment_container, fragment, FRAG1_TAG); 
} 

Si prega di notare che il quadro si aggiunge una nuova istanza di FragmentOne sul cambiamento dell'orientamento a meno che non si chiama setRetainInstance (vero), nel qual caso si aggiungerà la vecchia istanza di FragmentOne.

Problemi correlati