web-dev-qa-db-fra.com

Afficher DialogFragment de onActivityResult

J'ai le code suivant dans mon onActivityResult pour un de mes fragments:

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

Cependant, j'obtiens l'erreur suivante:

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

Quelqu'un sait-il ce qui se passe ou comment résoudre ce problème? Je dois noter que j'utilise le Android Support Package.

78
Kurtis Nusbaum

Si vous utilisez Android, la méthode onResume n'est pas le bon endroit où jouer avec des fragments. Vous devez le faire dans la méthode onResumeFragments, voir la description de la méthode onResume: http: //developer.Android.com/reference/Android/support/v4/app/FragmentActivity.html#onResume%28%29

Donc, le bon code de mon point de vue devrait être:

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);
    }
  }
}
73
Arcao

EDIT: Pas un bug, mais plutôt une lacune dans le framework de fragments. La meilleure réponse à cette question est celle fournie par @Arcao ci-dessus.

---- Poste d'origine ----

En fait, c'est un bug conn avec le package de support (edit: pas vraiment un bug. Voir le commentaire de @ alex-lockwood). Un contournement posté dans les commentaires du rapport de bogue consiste à modifier la source de DialogFragment comme suit:

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

Notez qu'il s'agit d'un hack géant. La façon dont je l'ai fait était de créer mon propre fragment de dialogue avec lequel je pouvais m'inscrire à partir du fragment d'origine. Lorsque cet autre fragment de dialogue a fait des choses (comme être rejeté), il a dit à tous les auditeurs qu'il s'en allait. Je l'ai fait comme ça:

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

Alors maintenant, j'ai un moyen d'aviser le PlayerListFragment quand les choses se produisent. Notez qu'il est très important d'appeler correctement unregisterPasswordEnteredListener (dans le cas ci-dessus lorsque jamais le PlayerListFragment "disparaît") sinon ce fragment de boîte de dialogue pourrait essayer d'appeler des fonctions sur l'écouteur enregistré lorsque cet écouteur n'existe plus.

27
Kurtis Nusbaum

Le commentaire laissé par @ Natix est un one-liner rapide que certaines personnes peuvent avoir supprimé.

La solution la plus simple à ce problème consiste à appeler super.onActivityResult () AVANT d'exécuter votre propre code. Cela fonctionne que vous utilisiez la bibliothèque de support ou non et conserve la cohérence comportementale de votre activité.

Il y a:

Plus j'en lis, plus les hacks insensés que j'ai vus.

Si vous rencontrez toujours des problèmes, alors celui de Alex Lockwood est celui à vérifier.

19
twig

Je pense qu'il s'agit d'un bug Android. Fondamentalement Android appelle onActivityResult à un mauvais moment dans le cycle de vie de l'activité/du fragment (avant onStart ()).

Le bogue est signalé à https://issuetracker.google.com/issues/36929762

Je l'ai résolu en stockant essentiellement l'intention en tant que paramètre que j'ai ensuite traité dans onResume ().

[EDIT] Il existe aujourd'hui de meilleures solutions à ce problème qui n'étaient pas disponibles en 2012. Voir les autres réponses.

14
hrnt

EDIT: Pourtant une autre option , et peut-être la meilleure encore (ou, au moins, ce que la bibliothèque de support attend ...)

Si vous utilisez DialogFragments avec la bibliothèque de support Android Android, vous devez utiliser une sous-classe de FragmentActivity. Essayez ce qui suit:

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
}

J'ai jeté un œil à source pour FragmentActivity, et il semble qu'il appelle un gestionnaire de fragments interne afin de reprendre les fragments sans perdre d'état.


J'ai trouvé une solution qui n'est pas répertoriée ici. Je crée un gestionnaire et démarre le fragment de boîte de dialogue dans le gestionnaire. Donc, éditez un peu votre code:

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
}

Cela me semble plus propre et moins hacky.

11
Simon Jacobs

Il existe deux méthodes DialogFragment show () - show(FragmentManager manager, String tag) et show(FragmentTransaction transaction, String tag).

Si vous souhaitez utiliser la version FragmentManager de la méthode (comme dans la question d'origine), une solution simple consiste à remplacer cette méthode et à utiliser commitAllowingStateLoss:

public class MyDialogFragment extends DialogFragment {

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

}

Remplacer show(FragmentTransaction, String) de cette façon n'est pas aussi facile car il devrait également modifier certaines variables internes dans le code DialogFragment d'origine, donc je ne le recommanderais pas - si vous souhaitez utiliser cette méthode, essayez les suggestions dans le réponse acceptée (ou le commentaire de Jeffrey Blattman).

Il y a un certain risque à utiliser commitAllowingStateLoss - la documentation indique "Comme commit () mais permet l'exécution de la validation après l'enregistrement de l'état d'une activité. Ceci est dangereux car la validation peut être perdue si l'activité doit être restaurée ultérieurement à partir de son état , cela ne doit donc être utilisé que dans les cas où l'état de l'interface utilisateur peut être modifié de manière inattendue pour l'utilisateur. "

9
gkee

Vous ne pouvez pas afficher la boîte de dialogue après une activité attachée appelée sa méthode onSaveInstanceState (). Évidemment, onSaveInstanceState () est appelé avant onActivityResult (). Vous devez donc afficher votre boîte de dialogue dans cette méthode de rappel OnResumeFragment (), vous n'avez pas besoin de remplacer la méthode show () de DialogFragment. J'espère que ceci vous aidera.

4
handrenliang

onActivityResult () s'exécute avant onResume (). Vous devez faire votre interface utilisateur dans onResume () ou version ultérieure.

Utilisez un booléen ou tout ce dont vous avez besoin pour communiquer qu'un résultat est revenu entre ces deux méthodes.

... C'est ça. Simple.

3
Eurig Jones

J'ai trouvé une troisième solution, basée en partie sur la solution de hmt. Fondamentalement, créez une ArrayList of DialogFragments, à afficher sur onResume ();

ArrayList<DialogFragment> dialogList=new ArrayList<DialogFragment>();

//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");
}
3
PearsonArtPhoto

Je sais que cela a été répondu il y a longtemps ... mais il existe un moyen beaucoup plus facile de le faire que certaines des autres réponses que j'ai vues ici ... Dans mon cas spécifique, je devais montrer un DialogFragment à partir d'un fragment surActivityResult () méthode.

Voici mon code pour gérer cela, et cela fonctionne à merveille:

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

Comme cela a été mentionné dans certains des autres articles, commettre une perte d'état peut causer des problèmes si vous ne faites pas attention ... dans mon cas, j'affichais simplement un message d'erreur à l'utilisateur avec un bouton pour fermer la boîte de dialogue, donc si le l'état de ce qui est perdu, ce n'est pas un gros problème.

J'espère que cela t'aides...

2
Justin

C'est une vieille question mais j'ai résolu par la manière la plus simple, je pense:

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

Cela se produit car lorsque #onActivityResult () est appelé, l'activité parent a déjà appelé #onSaveInstanceState ()

J'utiliserais un Runnable pour "enregistrer" l'action (afficher la boîte de dialogue) sur #onActivityResult () pour l'utiliser plus tard lorsque l'activité sera prête.

Avec cette approche, nous nous assurons que l'action que nous voulons fonctionnera toujours

@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;
    }
}
2
Ricard

il suffit d'appeler super.onActivityResult(requestCode, resultCode, data); avant de manipuler le fragment

0
Thomas Klammer

La solution la plus propre que j'ai trouvée est la suivante:

@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.
}
0
fhucho

J'ai eu cette erreur en faisant .show(getSupportFragmentManager(), "MyDialog"); en activité.

Essayez d'abord .show(getSupportFragmentManager().beginTransaction(), "MyDialog");.

Si cela ne fonctionne toujours pas, ce message ( Afficher DialogFragment de onActivityResult ) m'aide à résoudre le problème.

0
Youngjae

Autrement:

@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
}
0
Maher Abuthraa