82

私のフラグメントの onActivityResult に次のコードがあります。

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

ただし、次のエラーが発生します。

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

何が起こっているのか、またはこれを修正する方法を知っている人はいますか? Android サポート パッケージを使用していることに注意してください。

4

17 に答える 17

27

編集: バグではなく、フラグメント フレームワークの欠陥です。この質問に対するより良い答えは、上記の @Arcao によって提供されたものです。

---- 元の投稿 ----

実際、これはサポート パッケージの既知のバグです(編集: 実際にはバグではありません。@alex-lockwood のコメントを参照してください)。バグ レポートのコメントに投稿された回避策は、DialogFragment のソースを次のように変更することです。

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

これは巨大なハックであることに注意してください。私が実際に行った方法は、元のフラグメントから登録できる独自のダイアログ フラグメントを作成することでした。他のダイアログ フラグメントが何かを実行すると (破棄されるなど)、それがなくなることをすべてのリスナーに伝えます。私はこのようにしました:

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

これで、何かが起こったときに PlayerListFragment に通知する方法ができました。unregisterPasswordEnteredListener を適切に呼び出すことが非常に重要であることに注意してください (上記の場合、PlayerListFragment が「なくなる」場合)。そうしないと、リスナーが存在しなくなったときに、このダイアログ フラグメントが登録済みリスナーの関数を呼び出そうとする可能性があります。

于 2012-04-11T22:35:04.053 に答える
26

@Natixが残したコメントは、一部の人が削除した可能性がある簡単なワンライナーです。

この問題の最も簡単な解決策は、独自のコードを実行する前にsuper.onActivityResult()を呼び出すことです。これは、サポート ライブラリを使用しているかどうかに関係なく機能し、アクティビティの動作の一貫性を維持します。

がある:

これを読めば読むほど、私が見た非常識なハックが増えました。

それでも問題が発生する場合は、 Alex Lockwoodによる問題を確認してください。

于 2015-05-24T23:49:32.947 に答える
14

Androidのバグだと思います。基本的に、Android はアクティビティ/フラグメント ライフサイクルの間違った時点 (onStart() の前) で onActivityResult を呼び出します。

バグはhttps://issuetracker.google.com/issues/36929762で報告されています

基本的に、後で onResume() で処理したパラメーターとして Intent を保存することで解決しました。

[編集] 2012 年には利用できなかった、この問題に対するより良い解決策が最近あります。他の回答を参照してください。

于 2012-04-11T21:54:35.703 に答える
11

EDIT: Yet another option, and possibly the best yet (or, at least what the support library expects...)

If you're using DialogFragments with the Android support library, you should be using a subclass of FragmentActivity. Try the following:

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
}

I took a look at the source for FragmentActivity, and it looks like it's calling an internal fragment manager in order to resume fragments without losing state.


I found a solution that's not listed here. I create a Handler, and start the dialog fragment in the Handler. So, editing your code a bit:

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
}

This seems cleaner and less hacky to me.

于 2013-07-29T23:08:14.007 に答える
9

DialogFragment の show() メソッドには、 と の 2 つがありshow(FragmentManager manager, String tag)ますshow(FragmentTransaction transaction, String tag)

メソッドの FragmentManager バージョンを使用する場合 (元の質問のように)、簡単な解決策は、このメソッドをオーバーライドして 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)元のDialogFragmentコード内のいくつかの内部変数も変更する必要があるため、この方法をオーバーライドするのは簡単ではありません.ジェフリー・ブラットマン)。

commitAllowingStateLoss の使用にはリスクがあります。ドキュメントには「commit() と同様ですが、アクティビティの状態が保存された後にコミットを実行できます。アクティビティを後でその状態から復元する必要がある場合、コミットが失われる可能性があるため、これは危険です。 、したがって、これは、ユーザーの UI 状態が予期せず変更されても問題ない場合にのみ使用する必要があります。」

于 2013-04-25T03:46:35.710 に答える
4

You cannot show dialog after attached activity called its method onSaveInstanceState(). Obviously, onSaveInstanceState() is called before onActivityResult(). So you should show your dialog in this callback method OnResumeFragment(), you do not need to override DialogFragment's show() method. Hope this will help you.

于 2013-05-24T01:56:49.060 に答える
3

hmt のソリューションに部分的に基づいて、3 番目のソリューションを思いつきました。基本的に、DialogFragments の ArrayList を作成し、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");
}
于 2012-12-18T03:21:19.833 に答える
3

onActivityResult() は onResume() の前に実行されます。onResume() 以降で UI を実行する必要があります。

ブール値または必要なものを使用して、これらの両方のメソッド間で結果が返されたことを伝えます。

... それでおしまい。単純。

于 2013-07-29T14:52:42.410 に答える
2

これはかなり前に回答されたことを知っています..しかし、ここで見た他の回答よりもはるかに簡単な方法があります...私の特定のケースでは、フラグメント onActivityResult() から DialogFragment を表示する必要がありました方法。

これはそれを処理するための私のコードであり、美しく機能します:

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

他のいくつかの投稿で述べたように、注意しないとステート ロスでコミットすると問題が発生する可能性があります。その状態が失われても大したことではありません。

お役に立てれば...

于 2013-06-13T06:29:34.953 に答える
0

私が見つけた最もクリーンな解決策はこれです:

@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.
}
于 2013-11-03T13:59:19.390 に答える
-3

ご存知のように、この問題はonActivityResult()がonstart()の前に呼び出されているためです。このコードで行ったように、onActivityResult()の開始時にonstart()を呼び出すだけです

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      onStart();
      //write you code here
}
于 2015-04-08T06:01:08.290 に答える