2011-11-22 14 views
24

ho avere un'attività che chiama setContentView con questo XML:Frammenti, DialogFragment, e Screen Rotation

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="horizontal" 
    > 
    <fragment android:name="org.vt.indiatab.GroupFragment" 
     android:id="@+id/home_groups" 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" 
     android:layout_weight="1" /> 
      <..some other fragments ...> 
</LinearLayout> 

Il GroupFragment estende Frammento, e tutto va bene lì. Tuttavia, mostro un DialogFragment all'interno di GroupFragment. Questo mostra correttamente, TUTTAVIA quando lo schermo ruota, ottengo una chiusura forzata.

Qual è il modo corretto di visualizzare un oggetto DialogFragment da un altro frammento diverso da DialogFragment.show (FragmentManager, String)?

+0

Si può fornire uno stacktrace della forza di stretta? – NPike

+0

Puoi pubblicare la riga di codice in cui istanzia il tuo DialogFragment? –

risposta

11

OK, mentre il metodo di Zsombor funziona, questo è dovuto al fatto che sono inesperto con Fragments e la sua soluzione causa problemi con lo saveInstanceState Bundle.

Apparentemente (almeno per un DialogFragment), dovrebbe essere un public static class. DEVI anche scrivere il tuo metodo static DialogFragment newInstance(). Questo perché la classe Fragment chiama il metodo newInstance nel suo metodo instantiate().

Quindi, in conclusione, è necessario scrivere il DialogFragments in questo modo:

public static class MyDialogFragment extends DialogFragment { 

    static MyDialogFragment newInstance() { 
     MyDialogFragment d = new MyDialogFragment(); 
     return d; 
    } 

    @Override 
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
     ... 
    } 
} 

e mostrare loro con:

private void showMyDialog() { 
    MyDialogFragment d = MyDialogFragment.newInstance(); 
    d.show(getFragmentManager(), "dialog"); 
} 

Questo può essere unico per la Biblioteca ActionBarSherlock, ma i campioni ufficiali nel La documentazione SDK usa anche questo paradigma.

+0

Ho provato a far funzionare questo stesso approccio, ma ho comunque riscontrato un errore. Il codice sorgente è https://github.com/gauntface/Android-Boiler-Plate-Kitchen-Sink [.activity.DialogActivity] –

+15

Non è necessario scrivere "proprio" metodo newInstance() come Fragment.instantiate() sta chiamando [Class.newInstance()] (http://developer.android.com/reference/java/lang/Class.html#newInstance%28%29). Ma poiché l'istanza del frammento può essere istanziata usando Class.newInstance(), devi fornire un costruttore di default esplicito per ogni frammento che scrivi. –

+0

@SzabolcsBerecz: OK wow che ha appena aggiunto tanta chiarezza. Grazie mille. – Weston

54

C'è un errore nella libreria di compatibilità che può causare questo. Prova a mettere questo in voi dialogfragment:

@Override 
public void onDestroyView() { 
    if (getDialog() != null && getRetainInstance()) 
    getDialog().setOnDismissListener(null); 
    super.onDestroyView(); 
} 

Propongo inoltre di impostare il dialogfragment come trattenuta, in modo da non ottenere licenziato dopo la rotazione. Metti "setRetainInstance (true);" per esempio. nel metodo onCreate().

+10

setRetainInstance (true) corregge l'arresto anomalo, ma la finestra di dialogo viene semplicemente ignorata nella modifica dell'orientamento. Avendo sia setRetainInstance (true) che lo snippet di codice sopra verrà visualizzato nuovamente. L'unico problema è che in qualche modo il bundle di SaveInstanceState si sta incasinando. Non posso riportare i miei valori dopo il cambio di orientamento. Il pacchetto è SEMPRE nullo quando viene chiamato onCreateDialog(). Qualche idea? – Weston

+1

Questo risolve il mio compito di creare finestre di dialogo persistenti. Utilizzando la lib di compatibilità V4. Ma sei sicuro che usando l'API nativa 11 DialogFragment non ha bisogno di questa soluzione alternativa? –

+1

Non l'ho esaminato, poiché utilizzo solo la libreria del backport. Non sarei sorpreso se questo fosse ancora buggy nel livello API 11. Raccomando di utilizzare la lib del backport per qualsiasi cosa sotto ICS, in questo modo si può essere sicuri che il framework dei frammenti funzioni in modo coerente. –

0

Ho avuto un problema simile, tuttavia nessuno dei precedenti ha funzionato per me. Alla fine avevo bisogno di creare il frammento nel codice anziché nel layout XML.

See: Replacing fragments and orientation change

2

per superare la Bundle essere sempre nullo, ho salvarlo in un campo statico in onSaveInstanceState. È un odore di codice, ma l'unica soluzione che ho trovato sia per il ripristino della finestra di dialogo che per il salvataggio dello stato.

Il riferimento del pacchetto deve essere annullato in onDestroy.

@Override 
public void onCreate(Bundle savedInstanceState) 
{ 
    if (savedInstanceState == null) 
     savedInstanceState = HackishSavedState.savedInstanceState; 

    setRetainInstance(true); 
} 

@Override 
public Dialog onCreateDialog(Bundle savedInstanceState) 
{ 
    if (savedInstanceState == null) 
     savedInstanceState = HackishSavedState.savedInstanceState; 

    ... 
} 

@Override 
public void onDestroyView() // necessary for restoring the dialog 
{ 
    if (getDialog() != null && getRetainInstance()) 
     getDialog().setOnDismissListener(null); 

    super.onDestroyView(); 
} 

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

    HackishSavedState.savedInstanceState = outState; 
    super.onSaveInstanceState(outState); 
} 

@Override 
public void onDestroy() 
{ 
    HackishSavedState.savedInstanceState = null; 
    super.onDestroy(); 
} 

private static class HackishSavedState 
{ 
    static Bundle savedInstanceState; 
} 
+0

questa classe con membri statici rimarrà in memoria fino a quando il classloader di quella classe è attivo, ci vorrà del tempo ... – Eugene

1

Ho utilizzato un mix delle soluzioni presentate e aggiunto un'altra cosa. Questa è la mia soluzione finale:

Ho usato setRetainInstance (true) nel onCreateDialog; Ho usato questo:

public void onDestroyView() { 
    if (getDialog() != null && getRetainInstance()) 
     getDialog().setDismissMessage(null); 
    super.onDestroyView(); 
} 

E come una soluzione del savedInstanceState non funziona, ho creato una classe privata chiamata StateHolder (allo stesso modo in cui un supporto viene a creare per un controllo ListView):

private class StateHolder { 
    String name; 
    String quantity; 
} 

I salvare lo stato in questo modo:

@Override 
public void onSaveInstanceState(Bundle savedInstanceState) { 
    super.onSaveInstanceState(savedInstanceState); 
    stateHolder = new StateHolder(); 
    stateHolder.name = actvProductName.getText().toString(); 
    stateHolder.quantity = etProductQuantity.getText().toString(); 
} 

Nel metodo onDismiss ho impostato lo stateHolder di nuovo a zero. Quando viene creata la finestra di dialogo, verifica se stateHolder non è null per ripristinare lo stato o semplicemente inizializza tutto normalmente.

0

Mi sono imbattuto in questo sul mio progetto e nessuna delle soluzioni di cui sopra ha aiutato.

Se l'eccezione sembra qualcosa di simile


java.lang.RuntimeException: Unable to start activity ComponentInfo{ 

... 

     Caused by: java.lang.IllegalStateException: Fragment.... 
     did not create a view. 

E 'causata da un problema con un ID contenitore di ripiego che viene utilizzato dopo la rotazione. Vedere questo biglietto per maggiori dettagli:

https://code.google.com/p/android/issues/detail?id=18529

Fondamentalmente si può evitare che l'incidente facendo in modo tutti i frammenti XML hanno un tag definito nel layout. Ciò impedisce il verificarsi della condizione di fallback se si ruota quando un frammento è visibile.

Nel mio caso sono stato in grado di applicare questa correzione senza dovendo eseguire l'override suDestroyView() o setRetainInstance (true), che è la raccomandazione comune per questa situazione.

0

Ho riscontrato questo problema e il trucco onDestroyView() non funzionava. Si è scoperto che era perché stavo creando una finestra di dialogo piuttosto intensa in onCreate(). Ciò ha incluso il salvataggio di un riferimento allo AlertDialog, che poi restituirei in onCreateDialog().

Quando ho spostato tutto questo codice su onCreateDialog() e ho smesso di conservare un riferimento alla finestra di dialogo, ha iniziato a funzionare di nuovo. Mi aspetto che stia violando uno degli invarianti. DialogFragment riguarda la gestione della sua finestra di dialogo.

1

Ho risolto questo problema con le risposte di @ ZsomborErdődy-Nagy e @AndyDennie. È necessario creare una sottoclasse questa classe e in voi genitori frammento chiamata setRetainInstance(true) e dialogFragment.show(getFragmentManager(), "Dialog");

public class AbstractDialogFragment extends DialogFragment { 

     @Override 
     public void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
      setRetainInstance(true); 
     } 

     @Override 
     public void onDestroyView() { 
      if (getDialog() != null && getRetainInstance()) 
       getDialog().setDismissMessage(null); 
      super.onDestroyView(); 
     } 
    } 
+0

Ho visto che se l'app mostra un altro dialogfragment come l'app di googleplayservices si blocca anche sul cambio di orientamento. –

0

In onCreate() chiamata setRetainInstance(true) e quindi includere questo:

@Override 
public void onDestroyView() { 
    if (getDialog() != null && getRetainInstance()) { 
     getDialog().setOnDismissMessage(null); 
    } 
    super.onDestroyView(); 
} 

Quando si chiama setRetainInstance(true) in onCreate(), onCreate() non sarà più chiamato attraverso le modifiche di orientamento, ma onCreateView() verrà comunque chiamato.

Così si può ancora salvare lo stato al vostro pacco in onSaveInstanceState() e poi recuperarlo in onCreateView():

@Override 
public void onSaveInstanceState(Bundle outState) { 

    super.onSaveInstanceState(outState); 

    outState.putInt("myInt", myInt); 
} 

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

    View view = inflater.inflate(R.layout.my_layout, container); 

    if (savedInstanceState != null) { 

     myInt = savedInstanceState.getInt("myInt"); 
    } 

    ... 

    return view; 
}