Afficher le fragment de dialogue 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 ce qui se passe, ou comment je peux arranger ça? Je dois noter que j'utilise le pack de support Android.
17 réponses
si vous utilisez Android Support library, la méthode onResume n'est pas le bon endroit, où jouer avec des fragments. Vous devriez 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 code correct 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);
}
}
}
EDIT: Pas un bug, mais plutôt un défaut dans le cadre des fragments. La meilleure réponse à cette question est celle fournie par @Arcao ci-dessus.
---- message d'Origine ----
en fait c'est un bogue connu avec le paquet support (edit: not actually a bug. voir le commentaire de @alex-lockwood). Un travail posté dans les commentaires du rapport de bogue est de modifier la source du 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;
}
Note Ceci est un hack géant. La façon dont je l'ai fait, c'était juste faire mon propre fragment de dialogue que je pouvais enregistrer à partir du fragment original. Quand cet autre fragment de dialogue a fait des choses (comme être rejeté), il a dit à tous les auditeurs qu'il allait disparaître. Je l'ai fait comme ceci:
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;
}
}
donc maintenant j'ai un moyen de notifier le fragment Playerlist quand les choses se produisent. Notez que c'est très important que vous appeliez unnisterpasswordenteredlistener de manière appropriée (dans le cas ci-dessus lorsque le fragment PlayerListFragment "s'en va"), sinon ce fragment de dialogue pourrait essayer d'appeler des fonctions sur l'écouteur enregistré lorsque celui-ci n'existe plus.
le commentaire laissé par @Natix est un paquetage rapide que certaines personnes ont peut-être enlevé.
la solution la plus simple à ce problème est d'appeler super.onActivityResult () avant d'exécuter votre propre code. Cela fonctionne indépendamment du fait que vous utilisez ou non la bibliothèque de soutien et maintient la cohérence comportementale dans votre activité.
il y a:
- pas besoin de ajouter de faux retards en utilisant des threads, des handlers ou des sleeps .
- pas besoin commit allowing state loss ou sous-classe pour outrepasser show(). Ce n'est pas un bug, c'est un avertissement. Ne jetez pas les données d'état. ( un Autre , bonus exemple )
- Pas besoin de le garder piste de la boîte de dialogue des fragments dans votre activité (bonjour les fuites de mémoire!)
- Et par Dieu, pas besoin de s'amuser avec les activités du cycle de vie en appelant onStart() manuellement .
plus je lis dans ce les plus fous hacks que j'ai vu.
si vous rencontrez encore des problèmes, alors celui de Alex Lockwood est celui à vérifier.
- Pas besoin de écrire du code pour onSaveInstanceState() ( un Autre )
appels Android onActivityResult()
avant onStart()
.
Je l'ai résolu en stockant l'intention comme un paramètre que j'ai traité plus tard dans onResume()
.
il y a deux méthodes de dialogue show () - show(FragmentManager manager, String tag)
et show(FragmentTransaction transaction, String tag)
.
si vous voulez utiliser la version FragmentManager de la méthode( comme dans la question originale), une solution facile est de passer outre cette méthode et d'utiliser la perte de commitallowingstatt:
public class MyDialogFragment extends DialogFragment {
@Override
public void show(FragmentManager manager, String tag) {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitAllowingStateLoss();
}
}
Substitution show(FragmentTransaction, String)
de cette façon, n'est pas aussi facile, car il faut aussi modifier certaines variables internes au sein de l'origine DialogFragment code, donc je ne le recommande pas - si vous voulez utiliser cette méthode, puis essayez les suggestions accepté de répondre (ou le commentaire de Jeffrey Blattman).
l'utilisation de commitallowingstatteloss comporte un certain risque: la documentation indique" comme commit() mais permet d'exécuter la propagation après avoir sauvegardé l'état d'une activité. C'est dangereux parce que la propagation peut être perdue si l'activité doit être restaurée plus tard de son état, donc cela ne devrait être utilisé que pour les cas où il est correct pour L'état UI pour changer de façon inattendue sur l'utilisateur."
EDIT: Encore l'autre option, et peut-être le meilleur encore (ou du moins ce que la bibliothèque de prise en charge en attend...)
si vous utilisez DialogFragments avec la bibliothèque de support 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 oeil à la source 1519100920 "pour FragmentActivity, et il semble qu'il appelle un gestionnaire de fragment interne afin de reprendre les fragments sans perdre l'état.
j'ai trouvé une solution qui n'est pas listée ici. Je crée un gestionnaire, et démarre le fragment de dialogue dans le Gestionnaire. Donc, modifier 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 macabre.
vous ne pouvez pas afficher le dialogue après l'activité attachée appelée sa méthode onSaveInstanceState(). Évidemment, onSaveInstanceState () est appelé avant onActivityResult (). Vous devriez donc afficher votre dialogue dans cette méthode de callback OnResumeFragment (), vous n'avez pas besoin de surcharger la méthode show () de DialogFragment. Espérons que cela vous aidera.
j'ai trouvé une troisième solution, basée en partie sur la solution de hmt. Fondamentalement, créer un ArrayList de fragments de dialogues, à montrer 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");
}
onActivityResult() execute before onResume(). Vous devez faire votre UI dans onResume () ou plus tard.
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 tout. Simple.
je sais que cela a été répondu il y a un certain temps.. mais il y a une façon beaucoup plus facile de le faire que certaines des autres réponses que j'ai vu ici... Dans mon cas spécifique, j'ai dû montrer un fragment de dialogue à partir d'une méthode de fragment onActivityResult ().
C'est mon code pour gérer ça, et ça marche très bien:
DialogFragment myFrag; //Don't forget to instantiate this
FragmentTransaction trans = getActivity().getSupportFragmentManager().beginTransaction();
trans.add(myFrag, "MyDialogFragmentTag");
trans.commitAllowingStateLoss();
comme il a été mentionné dans certains des autres messages, commettre avec perte d'état peut causer des problèmes si vous n'êtes pas prudent... dans dans mon cas, j'affichais simplement un message d'erreur à l'utilisateur avec un bouton pour fermer le dialogue, donc si l'état de celui-ci est perdu, ce n'est pas une grosse affaire.
Espérons que cette aide...
c'est une vieille question mais j'ai résolu par le moyen le plus simple, je pense:
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
MsgUtils.toast(getString(R.string.msg_img_saved),
getActivity().getApplicationContext());
}
});
la solution la plus propre que j'ai trouvée est celle-ci:
@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.
}
j'ai eu cette erreur en faisant .show(getSupportFragmentManager(), "MyDialog");
en activité.
Essayer .show(getSupportFragmentManager().beginTransaction(), "MyDialog");
en premier.
S'il ne fonctionne toujours pas, ce post ( montrer le fragment de dialogue de onActivityResult ) m'aide à résoudre le problème.
une autre voie:
@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
}
il suffit d'appeler super.onActivityResult(requestCode, resultCode, data);
avant de manipuler le fragment
cela arrive parce que quand #onActivityResult () est appelé, l'activité mère a déjà appelé #onSaveInstanceState ()
j'utiliserais un Runnable pour" sauvegarder " l'action (Afficher la boîte de dialogue) sur #onActivityResult() pour l'utiliser plus tard lorsque l'activité est prête.
Avec cette approche, nous assurez-vous que l'action que nous voulons toujours travailler
@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;
}
}
Comme vous le savez tous ce problème est en raison de l'onActivityResult() appelle avant onstart(),donc il suffit d'appeler onstart() au début de onActivityResult() comme j'ai fait dans ce code
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
onStart();
//write you code here
}