Mostra DialogFragment da onActivityResult

Ho il seguente codice nel mio onActivityResult per un mio frammento:

onActivityResult(int requestCode, int resultCode, Intent data){ //other code ProgressFragment progFragment = new ProgressFragment(); progFragment.show(getActivity().getSupportFragmentManager(), PROG_DIALOG_TAG); // other code } 

Tuttavia, sto ricevendo il seguente errore:

 Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState 

Qualcuno sa cosa sta succedendo o come posso risolvere questo problema? Devo notare che sto usando il pacchetto di supporto Android.

Se si utilizza la libreria di supporto Android, il metodo onResume non è il posto giusto, dove giocare con i frammenti. Dovresti farlo nel metodo onResumeFragments, consulta la descrizione del metodo onResume: http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume%28%29

Quindi il codice corretto dal mio punto di vista dovrebbe essere:

 private boolean mShowDialog = false; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data){ super.onActivityResult(requestCode, resultCode, data); // remember that dialog should be shown mShowDialog = true; } @Override protected void onResumeFragments() { super.onResumeFragments(); // play with fragments here if (mShowDialog) { mShowDialog = false; // Show only if is necessary, otherwise FragmentManager will take care if (getSupportFragmentManager().findFragmentByTag(PROG_DIALOG_TAG) == null) { new ProgressFragment().show(getSupportFragmentManager(), PROG_DIALOG_TAG); } } } 

EDIT: non un bug, ma più di una carenza nel framework dei frammenti. La risposta migliore a questa domanda è quella fornita da @Arcao sopra.

—- Post originale —-

In realtà è un bug noto con il pacchetto di supporto (edit: non in realtà un bug, vedi il commento di @ alex-lockwood). Un lavoro postato nei commenti del bug report è di modificare la sorgente del DialogFragment in questo modo:

 public int show(FragmentTransaction transaction, String tag) { return show(transaction, tag, false); } public int show(FragmentTransaction transaction, String tag, boolean allowStateLoss) { transaction.add(this, tag); mRemoved = false; mBackStackId = allowStateLoss ? transaction.commitAllowingStateLoss() : transaction.commit(); return mBackStackId; } 

Nota questo è un hack gigante. Il modo in cui l’ho fatto è stato creare il mio frammento di finestra di dialogo che potevo registrare dal frammento originale. Quando quell’altro frammento di dialogo ha fatto le cose (come essere licenziato), ha detto a tutti gli ascoltatori che stava andando via. L’ho fatto in questo modo:

 public static class PlayerPasswordFragment extends DialogFragment{ Player toJoin; EditText passwordEdit; Button okButton; PlayerListFragment playerListFragment = null; public void onCreate(Bundle icicle){ super.onCreate(icicle); toJoin = Player.unbundle(getArguments()); Log.d(TAG, "Player id in PasswordFragment: " + toJoin.getId()); } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle){ View v = inflater.inflate(R.layout.player_password, container, false); passwordEdit = (EditText)v.findViewById(R.id.player_password_edit); okButton = (Button)v.findViewById(R.id.ok_button); okButton.setOnClickListener(new View.OnClickListener(){ public void onClick(View v){ passwordEntered(); } }); getDialog().setTitle(R.string.password_required); return v; } public void passwordEntered(){ //TODO handle if they didn't type anything in playerListFragment.joinPlayer(toJoin, passwordEdit.getText().toString()); dismiss(); } public void registerPasswordEnteredListener(PlayerListFragment playerListFragment){ this.playerListFragment = playerListFragment; } public void unregisterPasswordEnteredListener(){ this.playerListFragment = null; } } 

Quindi ora ho un modo per notificare a PlayerListFragment quando accadono le cose. Si noti che è molto importante chiamare unregisterPasswordEnteredListener in modo appropriato (nel caso precedente quando mai PlayerListFragment “si allontana”) altrimenti questo frammento di finestra di dialogo potrebbe provare a chiamare funzioni sul listener registrato quando quel listener non esiste più.

Android chiama onActivityResult() prima di onStart() .

L’ho risolto memorizzando fondamentalmente l’Intent come parametro successivamente elaborato in onResume() .

Il commento lasciato da @Natix è un veloce one-liner che alcune persone potrebbero aver rimosso.

La soluzione più semplice a questo problema è chiamare super.onActivityResult () PRIMA di eseguire il proprio codice. Questo funziona indipendentemente dal fatto che tu stia utilizzando la libreria di supporto o meno e mantenga la coerenza comportamentale nella tua attività.

C’è:

  • Non c’è bisogno di aggiungere ritardi faux usando thread, gestori o posti letto .
  • Non è necessario impegnarsi per consentire la perdita di stato o la sottoclass per sovrascrivere show (). NON è un bug, è un avvertimento. Non buttare via dati di stato. ( Un altro , esempio bonus )
  • Non è necessario tenere traccia dei frammenti di dialogo nella tua attività (ciao perdite di memoria!)
  • E per Dio, non c’è bisogno di scherzare con il ciclo di vita delle attività chiamando onStart () manualmente .

Più leggo in questo gli hack più folli che ho visto.

Se stai ancora riscontrando problemi, è quello che controlla Alex Lockwood .

  • Non c’è bisogno di scrivere alcun codice per onSaveInstanceState () ( Un altro )

Esistono due metodi show () di DialogFragment: show(FragmentManager manager, String tag) e show(FragmentTransaction transaction, String tag) .

Se si desidera utilizzare la versione di FragmentManager del metodo (come nella domanda originale), una soluzione semplice è sovrascrivere questo metodo e utilizzare commitAllowingStateLoss:

 public class MyDialogFragment extends DialogFragment { @Override public void show(FragmentManager manager, String tag) { FragmentTransaction ft = manager.beginTransaction(); ft.add(this, tag); ft.commitAllowingStateLoss(); } } 

show(FragmentTransaction, String) overriding show(FragmentTransaction, String) questo modo non è così facile perché dovrebbe anche modificare alcune variabili interne all’interno del codice DialogFragment originale, quindi non lo consiglierei – se vuoi usare quel metodo, prova i suggerimenti nell’accettato risposta (o il commento di Jeffrey Blattman).

C’è qualche rischio nell’usare commitAllowingStateLoss – la documentazione afferma “Like commit () ma permette che il commit venga eseguito dopo che lo stato di un’attività è stato salvato. Questo è pericoloso perché il commit può essere perso se l’attività deve essere ripristinata in seguito dal suo stato , quindi dovrebbe essere utilizzato solo nei casi in cui è corretto che lo stato dell’interfaccia utente cambi in modo imprevisto per l’utente. ”

EDIT: ancora un’altra opzione, e forse il migliore ancora (o almeno quello che la biblioteca di supporto si aspetta …)

Se stai utilizzando DialogFragments con la libreria di supporto Android, dovresti utilizzare una sottoclass di FragmentActivity. Prova quanto segue:

 onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, intent); //other code ProgressFragment progFragment = new ProgressFragment(); progFragment.show(getActivity().getSupportFragmentManager(), PROG_DIALOG_TAG); // other code } 

Ho dato un’occhiata all’origine di FragmentActivity e sembra che stia chiamando un gestore di frammenti interno per riprendere i frammenti senza perdere lo stato.


Ho trovato una soluzione che non è elencata qui. Creo un gestore e avvio il frammento della finestra di dialogo nel gestore. Quindi, modifica il tuo codice un po ‘:

 onActivityResult(int requestCode, int resultCode, Intent data) { //other code final FragmentManager manager = getActivity().getSupportFragmentManager(); Handler handler = new Handler(); handler.post(new Runnable() { public void run() { ProgressFragment progFragment = new ProgressFragment(); progFragment.show(manager, PROG_DIALOG_TAG); } }); // other code } 

Questo mi sembra più pulito e meno hacky.

Non è ansible visualizzare la finestra di dialogo dopo l’attività collegata denominata metodo suSaveInstanceState (). Ovviamente, onSaveInstanceState () viene chiamato prima onActivityResult (). Quindi dovresti mostrare la tua finestra di dialogo in questo metodo di callback OnResumeFragment (), non è necessario sovrascrivere il metodo show () di DialogFragment. Spero che questo ti possa aiutare.

Ho trovato una terza soluzione, basata parzialmente sulla soluzione di hmt. Fondamentalmente, crea un ArrayList di DialogFragments, da visualizzare su onResume ();

 ArrayList dialogList=new ArrayList(); //Some function, like onActivityResults { DialogFragment dialog=new DialogFragment(); dialogList.add(dialog); } protected void onResume() { super.onResume(); while (!dialogList.isEmpty()) dialogList.remove(0).show(getSupportFragmentManager(),"someDialog"); } 

onActivityResult () viene eseguito prima di onResume (). Devi eseguire la tua interfaccia utente in onResume () o successivo.

Usa un booleano o qualsiasi altra cosa tu abbia bisogno per comunicare che un risultato è tornato tra entrambi questi metodi.

… Questo è tutto. Semplice.

So che è stata data risposta abbastanza tempo fa .. ma c’è un modo molto più semplice per farlo rispetto ad alcune delle altre risposte che ho visto qui … Nel mio caso specifico, avevo bisogno di mostrare un DialogFragment da un frammento suActivityResult () metodo.

Questo è il mio codice per gestirlo, e funziona magnificamente:

 DialogFragment myFrag; //Don't forget to instantiate this FragmentTransaction trans = getActivity().getSupportFragmentManager().beginTransaction(); trans.add(myFrag, "MyDialogFragmentTag"); trans.commitAllowingStateLoss(); 

Come è stato menzionato in alcuni degli altri post, commettere una perdita di stato può causare problemi se non si presta attenzione … nel mio caso stavo semplicemente visualizzando un messaggio di errore all’utente con un pulsante per chiudere la finestra di dialogo, quindi se il lo stato di ciò è perso non è un grosso problema.

Spero che questo ti aiuti…

È una vecchia domanda, ma ho risolto il modo più semplice, penso:

 getActivity().runOnUiThread(new Runnable() { @Override public void run() { MsgUtils.toast(getString(R.string.msg_img_saved), getActivity().getApplicationContext()); } }); 

La soluzione più pulita che ho trovato è questa:

 @Override public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { new Handler().post(new Runnable() { @Override public void run() { onActivityResultDelayed(requestCode, resultCode, data); } }); } public void onActivityResultDelayed(int requestCode, int resultCode, Intent data) { // Move your onActivityResult() code here. } 

Ho ricevuto questo errore mentre facevo .show(getSupportFragmentManager(), "MyDialog"); in attività.

Prova .show(getSupportFragmentManager().beginTransaction(), "MyDialog"); primo.

Se ancora non funziona, questo post ( Mostra DialogFragment da onActivityResult ) mi aiuta a risolvere il problema.

Un altro modo:

 @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case Activity.RESULT_OK: new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message m) { showErrorDialog(msg); return false; } }).sendEmptyMessage(0); break; default: super.onActivityResult(requestCode, resultCode, data); } } private void showErrorDialog(String msg) { // build and show dialog here } 

basta chiamare super.onActivityResult(requestCode, resultCode, data); prima di maneggiare il frammento

Succede perché quando viene chiamato #onActivityResult (), l’attività padre ha già chiamato #onSaveInstanceState ()

Vorrei usare un Runnable per “salvare” l’azione (mostra la finestra di dialogo) su #onActivityResult () per usarla più tardi quando l’attività è stata pronta.

Con questo approccio ci assicuriamo che l’azione che vogliamo funzionerà sempre

 @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == YOUR_REQUEST_CODE) { mRunnable = new Runnable() { @Override public void run() { showDialog(); } }; } else { super.onActivityResult(requestCode, resultCode, data); } } @Override public void onStart() { super.onStart(); if (mRunnable != null) { mRunnable.run(); mRunnable = null; } } 

Come tutti sapete, questo problema è dovuto al fatto che onActivityResult () sta chiamando prima di onstart (), quindi chiamate onstart () all’inizio di onActivityResult () come ho fatto in questo codice

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { onStart(); //write you code here }