onActivityResultからDialogFragmentを表示


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サポートパッケージを使用していることに注意してください。

回答:


75

Androidサポートライブラリを使用している場合、onResumeメソッドはフラグメントを操作する適切な場所ではありません。onResumeFragmentsメソッドで実行する必要があります。onResumeメソッドの説明を参照してください:http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume%28%29

したがって、私の観点からの正しいコードは次のようになります。

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

13
+1、これは正解です。クラスにonResumeFragments()は存在しないことに注意してくださいActivity。基本を使用しているActivity場合は、onPostResume()代わりにを使用する必要があります。
Alex Lockwood

3
このソリューションを実装する前に、これを読んで、ハッキングである理由を確認してください。この質問の別の解決策のコメントには、はるかに単純な解決策が隠されています。
小枝2015年

1
super.onActivityResultを呼び出しても、IllegalStateExceptionが防止されないため、subjは修正されません。問題
demaksee 2015年

1
この質問はこの問題についてグーグルで最初にヒットしたものですが、受け入れられた答えは私の意見では最良のものではありません。代わりに、この回答を受け入れる必要があります:stackoverflow.com/a/30429551/1226020
JHH 2017年

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が「なくなる」場合)。そうしないと、このダイアログフラグメントは、登録されたリスナーが存在しなくなったときに、そのリスナーの関数を呼び出そうとする可能性があります。


3
ソースをコピーする必要のないソリューション...オーバーライドしてshow()、をキャッチするだけIllegalStateExceptionです。
Jeffrey Blattman 2012年

1
DialogFragmentのソースを変更するにはどうすればよいですか?または、投稿の最後に記載されているソリューションを投稿できますか?
ピョートルŚlesarew

1
@PeterSlesarew私は(かなり具体的な)ソリューションを投稿しました。
Kurtis Nusbaum 2013

9
ああ、これはバグではありません!内部でフラグメントトランザクションを実行するのは安全ではないため、Androidフレームワークは意図的に例外をスローしていますonActivityResult()!代わりに、このソリューションをお試しください:stackoverflow.com/questions/16265733/...
アレックス・ロックウッド

2
@AlexLockwoodこの質問が行われたとき、ドキュメントはこれについて警告していませんでした。あなたのソリューションが良いに見えながら、また、、それが戻って4月、2012年では動作しませんでしたonPostResumeし、onResumeFragmentsサポートライブラリに比較的新しい追加の両方です。
hrnt 2014年

24

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

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

有る:

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

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


相続があるとどうなりますか?superを呼び出す前に最初にコードを実行する場合は、super.onActivityResult()を呼び出すと問題が発生する可能性があります。スーパークラスは、onActivityResult内に独自のコードを持っている可能性があります。注意してください。
リカール

super.onActivityResult(requestCode, resultCode, data)コードの前に追加していますが、問題が修正されました。ただし、継承を追加したり、デフォルトのonActivityResultをオーバーライドしたりする場合は、onStart / onResumeを手動で処理する必要があります
mochadwi

14

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

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

基本的に、後でonResume()で処理するパラメーターとしてインテントを格納することで解決しました。

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


8
実際、それは実際にはバグではありません。そこのコメントで指摘されているように、それonActivityResult()は以前に呼ばれたことを明確に述べていますonResume()
Kurtis Nusbaum

2
バグに対する最後のコメントを読みましたか?バグは、onActivityResult()がonStart()の前に呼び出されることであり、onResume()の前に呼び出されることではありません。
hrnt 2012

ああ、そうです、それも本当です。それを逃した。私はまだ他のバグレポートが私の問題にもう少し関連していると信じていますが。
Kurtis Nusbaum 2012

onActivityResultが呼び出されるときに明確に定義されます。したがって、場合によっては不適切に見える場合でも、バグになることはありません。
sstn

1
@sstn、詳しく教えていただけますか?onActivityResultが呼び出されたとき(= onResumeの直前)に明確に定義されます。Androidは、onResumeの直前にonActivityResultを呼び出しません。したがって、これはバグです。
hrnt

11

編集:さらに別のオプション、そしておそらくこれまでで最高のオプション(または少なくともサポートライブラリが期待するもの...)

AndroidサポートライブラリでDialogFragmentsを使用している場合は、FragmentActivityのサブクラスを使用する必要があります。次のことを試してください。

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
}

FragmentActivityのソースを調べたところ、状態を失うことなくフラグメントを再開するために、内部フラグメントマネージャーを呼び出しているようです。


ここに記載されていない解決策を見つけました。ハンドラーを作成し、ハンドラーでダイアログフラグメントを開始します。したがって、コードを少し編集します。

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
}

これは私にはすっきりしていてハッキーではないようです。


5
ハンドラーを使用してこの問題を解決すると、遅延が追加されるだけなので、問題が発生する可能性は低くなります。しかし、問題がなくなることを保証するものではありません!これは、を使用して競合状態を解決するのと少し似ていThread#sleep()ます。
Alex Lockwood

27
呼び出すことsuper.onActivityResult()は、そこにある最も簡単な解決策であり、おそらく受け入れられた答えであるはずです!偶然にスーパーコールがないことに気づき、それを追加するだけでうまくいったことに嬉しい驚きを覚えました。これにより、このページに記載されている古いハックの1つを削除することができました(ダイアログを一時変数に保存して、に表示しますonResume())。
Natix 2013年

素晴らしい解決策。唯一の問題は、onActivityResult()フラグメントが結果を処理したかどうかを示す値を返さないことです。
マイケル

私のプロジェクトでsuper.onActivityResult()はない軟膏IllegalStateExceptionがクラッシュを呼び出す
demaksee

9

2つのDialogFragmentshow()メソッドがあります-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状態が予期せず変更されても問題がない場合にのみ使用する必要があります。」


4

メソッドonSaveInstanceState()を呼び出したアクティビティがアタッチされた後、ダイアログを表示することはできません。明らかに、onSaveInstanceState()はonActivityResult()の前に呼び出されます。したがって、このコールバックメソッドOnResumeFragment()でダイアログを表示する必要があります。DialogFragmentのshow()メソッドをオーバーライドする必要はありません。これがお役に立てば幸いです。


3

私は、hmtのソリューションに部分的に基づいて、3番目のソリューションを思いつきました。基本的に、onResume();に表示されるDialogFragmentsのArrayListを作成します。

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

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

ブール値または必要なものを使用して、これらの両方の方法の間に結果が戻ってきたことを伝えます。

... それでおしまい。シンプル。


2

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

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

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

他のいくつかの投稿で言及されているように、注意しないと状態損失でコミットすると問題が発生する可能性があります...私の場合、ダイアログを閉じるためのボタンでユーザーにエラーメッセージを表示していたので、その状態が失われることは大したことではありません。

お役に立てれば...


2

これは古い質問ですが、私は最も簡単な方法で解決したと思います。

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

2

これは、#onActivityResult()が呼び出されたときに、親アクティビティがすでに#onSaveInstanceState()を呼び出しているために発生します。

Runnableを使用して#onActivityResult()のアクション(ダイアログの表示)を「保存」し、後でアクティビティの準備ができたときに使用します。

このアプローチにより、私たちが望む行動が常に機能することを確認します

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

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

0

.show(getSupportFragmentManager(), "MyDialog");アクティビティ中にこのエラーが発生しました。

.show(getSupportFragmentManager().beginTransaction(), "MyDialog");最初に試してください。

それでも機能しない場合は、この投稿(onActivityResultのShow DialogFragment)が問題の解決に役立ちます。


0

別の方法:

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


-3

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

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

Androidのライフサイクルメソッドを直接呼び出さないでください。それらはシステムによってのみ呼び出されるべきです。
Chantell Osejo 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.