ダイアログを表示すると、「onSaveInstanceStateの後でこのアクションを実行できません」というメッセージが表示されます


121

一部のユーザーは報告していますが、通知バーのクイックアクションを使用すると、強制的に終了します。

「TestDialog」クラスを呼び出す通知にクイックアクションを表示します。「snooze」ボタンを押した後のTestDialogクラスで、SnoozeDialogを表示します。

private View.OnClickListener btnSnoozeOnClick() {
    return new View.OnClickListener() {

        public void onClick(View v) {
            showSnoozeDialog();
        }
    };
}

private void showSnoozeDialog() {
    FragmentManager fm = getSupportFragmentManager();
    SnoozeDialog snoozeDialog = new SnoozeDialog();
    snoozeDialog.show(fm, "snooze_dialog");
}

エラーは *IllegalStateException: Can not perform this action after onSaveInstanceState*.

IllegarStateExceptionが発生するコード行は次のとおりです。

snoozeDialog.show(fm, "snooze_dialog");

クラスは「FragmentActivity」を拡張し、「SnoozeDialog」クラスは「DialogFragment」を拡張しています。

エラーの完全なスタックトレースは次のとおりです。

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1327)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1338)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
at android.support.v4.app.DialogFragment.show(DialogFragment.java:127)
at com.test.testing.TestDialog.f(TestDialog.java:538)
at com.test.testing.TestDialog.e(TestDialog.java:524)
at com.test.testing.TestDialog.d(TestDialog.java:519)
at com.test.testing.g.onClick(TestDialog.java:648)
at android.view.View.performClick(View.java:3620)
at android.view.View$PerformClick.run(View.java:14292)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4507)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)

このエラーは再現できませんが、多くのエラーレポートが表示されます。

このエラーを修正するにはどうすればよいですか?


2
解決策を見つけましたか?私はあなたと同じ問題を抱えています。私はここで尋ねました:stackoverflow.com/questions/15730878/… 私の質問を確認して、私のケースでは機能しない可能性のある解決策を確認してください。多分それはあなたのために働くでしょう。
rootpanthera

いいえ、まだ解決しない:-(そして、あなたの提案は、すでに私のクラスに追加されます。
chrisonline

採用された回答はこちらからご確認ください。これは私の問題を解決: stackoverflow.com/questions/14177781/...
ボグダン

4
このダイアログがトリガーされたときにアクティビティが表示されていますか?これは、一時停止/停止されたアクティビティにアタッチされたダイアログをアプリが表示しようとしたことが原因であると思われます。
カイ

stackoverflow.com/questions/7575921/…あなたはこれを確実に試しました。
Orion

回答:


66

これは一般的な問題です。show()をオーバーライドし、DialogFragment拡張クラスで例外を処理することで、この問題を解決しました

public class CustomDialogFragment extends DialogFragment {

    @Override
    public void show(FragmentManager manager, String tag) {
        try {
            FragmentTransaction ft = manager.beginTransaction();
            ft.add(this, tag);
            ft.commit();
        } catch (IllegalStateException e) {
            Log.d("ABSDIALOGFRAG", "Exception", e);
        }
    }
}

このメソッドを適用しても、DialogFragment.classの内部フィールドは変更されないことに注意してください。

boolean mDismissed;
boolean mShownByMe;

これにより、予期しない結果が生じる場合があります。commit()の代わりにcommitAllowingStateLoss()を使用することをお勧めします


3
しかし、なぜこの問題が発生するのですか?エラーを無視しても大丈夫ですか?するとどうなりますか?結局のところ、クリックすると、アクティビティは正常に動作していることを意味します...とにかく、これをバグと見なしているため、ここで報告しました:code.google.com/p/android/issues/detail?id= 207269
Android開発者、

1
そこで、スターを付けたり、コメントしたりできますか?
Android開発者

2
try-catch句内でsuper.show(manager、tag)を呼び出す方が適切です。DialogFragmentが所有するフラグは、この方法で安全を保つことができます
Shayan_Aryan 2017年

19
この時点で、commit()の代わりにcommitAllowingStateLoss()を呼び出すことができます。例外は発生しません。
ARLabs 2017年

1
@ARLabs私がこの場合に想像するのは、ここに示すように例外をキャッチしただけではダイアログが表示されないため、回答者にとっても良いでしょう。可能な場合はダイアログを表示し、状態を復元する必要がある場合はダイアログが表示されないようにすることをお勧めします。また、バックグラウンドでアプリのメモリ使用量を低く抑えて、アプリが破壊されないようにします。
androidguy

27

つまり、commit()show()DialogFragmentの場合)の後のフラグメントonSaveInstanceState()

Androidはフラグメントの状態をに保存しますonSaveInstanceState()。したがって、commit()断片化した後にonSaveInstanceState()断片化すると、状態が失われます。

その結果、アクティビティが強制終了され、後で再作成された場合、フラグメントはアクティビティに追加されず、ユーザーエクスペリエンスが低下します。そのため、Androidはいかなる場合でも州の損失を認めていません。

簡単な解決策は、状態がすでに保存されているかどうかを確認することです。

boolean mIsStateAlreadySaved = false;
boolean mPendingShowDialog = false;

@Override
public void onResumeFragments(){
    super.onResumeFragments();
    mIsStateAlreadySaved = false;
    if(mPendingShowDialog){
        mPendingShowDialog = false;
        showSnoozeDialog();
    }
}

@Override
public void onPause() {
    super.onPause();
    mIsStateAlreadySaved = true;
}

private void showSnoozeDialog() {
    if(mIsStateAlreadySaved){
        mPendingShowDialog = true;
    }else{
        FragmentManager fm = getSupportFragmentManager();
        SnoozeDialog snoozeDialog = new SnoozeDialog();
        snoozeDialog.show(fm, "snooze_dialog");
    }
}

注:onResumeFragments()は、フラグメントが再開すると呼び出されます。


1
別のフラグメント内にDialogFragmentを表示したい場合はどうなりますか?
Android開発者

私たちのソリューションは、アクティビティとフラグメントの基本クラスを作成し、onResumeFragmentsをフラグメント化するように委譲します(フラグメントの基本クラスでonResumeFragmentsを作成します)。それは良い解決策ではありませんが、うまくいきます。より良い解決策がある場合は、私に知らせてください:)
Pongpat

さて、フラグメントが確実に表示されているので、「onStart」でダイアログを表示することはうまくいくはずだと思いましたが、それでもいくつかのクラッシュレポートが表示されます。代わりに「onResume」に配置するように指示されました。代替案について、私はこれを見ました:twigstechtips.blogspot.co.il/2014/01/…、しかしそれはかなり奇妙です。
Android開発者

twigstechtips.blogspot.co.il/2014/01/…が機能する理由は、新しいスレッドが開始されるため、コードrunOnUiThreadが実行される前に呼び出されるすべてのライフサイクルコード、つまりonStart、onResumeなどが原因です。つまり、状態はrunOnUiThreadが呼び出される前にすでに復元されています。
Pongpat

2
post(runnable)への単一の呼び出しを使用します。getFragmentManagerについては、状況によって異なります。そのダイアログを別のアクティビティと共有する場合は、getFragmentManagerを使用する必要がありますが、そのダイアログがフラグメントgetChildFragmentManagerでのみ存在する場合は、より良い選択のようです。
Pongpat

16
private void showSnoozeDialog() {
    FragmentManager fm = getSupportFragmentManager();
    SnoozeDialog snoozeDialog = new SnoozeDialog();
    // snoozeDialog.show(fm, "snooze_dialog");
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(snoozeDialog, "snooze_dialog");
    ft.commitAllowingStateLoss();
}

ref:リンク


11

数日後、解決方法を共有したいと思います。DialogFragmentを表示するには、そのshow()メソッドをオーバーライドcommitAllowingStateLoss()してTransactionオブジェクトを呼び出す必要があります。これはKotlinの例です。

override fun show(manager: FragmentManager?, tag: String?) {
        try {
            val ft = manager?.beginTransaction()
            ft?.add(this, tag)
            ft?.commitAllowingStateLoss()
        } catch (ignored: IllegalStateException) {

        }

    }

1
開発者がDialogFragmentあなたから継承する必要がないように、これを次のシグネチャを持つKotlin拡張関数に変更できますfun DialogFragment.showAllowingStateLoss(fragmentManager: FragmentManager, tag: String)。また、commitAllowingStateLoss()メソッドではなくメソッドを呼び出すため、try-catchは必要ありませんcommit()
アディルフセイン

10

ダイアログがそれほど重要ではない場合(アプリが閉じている/表示されなくなったときにダイアログを非表示にしても問題ありません)、以下を使用します。

boolean running = false;

@Override
public void onStart() {
    running = true;
    super.onStart();
}

@Override
public void onStop() {
    running = false;
    super.onStop();
}

そして、私たちが実行しているときにのみダイアログ(フラグメント)を開きます:

if (running) {
    yourDialog.show(...);
}

おそらくより優れたソリューションの編集:

ライフサイクルでonSaveInstanceStateが呼び出されることが予測できない場合、より良い解決策は次のようにisSavedInstanceStateDone()を確認することです:

/**
 * True if SavedInstanceState was done, and activity was not restarted or resumed yet.
 */
private boolean savedInstanceStateDone;

@Override
protected void onResume() {
    super.onResume();

    savedInstanceStateDone = false;
}

@Override
protected void onStart() {
    super.onStart();

    savedInstanceStateDone = false;
}

protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    savedInstanceStateDone = true;
}


/**
 * Returns true if SavedInstanceState was done, and activity was not restarted or resumed yet.
 */
public boolean isSavedInstanceStateDone() {
    return savedInstanceStateDone;
}

「onStart」メソッド呼び出しでこの例外が発生するため、これは機能していないようです(DialogFragmentをそこに表示しようとしました)。
Android開発者

あなたは私の日を救った。フランク、ありがとう。
Cüneyt

6

FragmentManagerの代わりにFragmentTransactionを使用してみてください。以下のコードで問題が解決すると思います。そうでない場合は、お知らせください。

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
SnoozeDialog snoozeDialog = new SnoozeDialog();
snoozeDialog.show(ft, "snooze_dialog");

編集:

フラグメント取引

こちらのリンクをご確認ください。それはあなたの質問を解決すると思います。


4
FragmentTransactionを使用して問題が修正される理由についての説明はすばらしいでしょう。
Hemanshu 2014

3
Dialog#show(FragmentManager、tag)も同じことを行います。これは解決策ではありません。
ウィリアム

3
この答えは解決策ではありません。DialogFragment#show(ft)とshow(fm)はまったく同じことを行います。
danijoo 2015

@danijooその両方が同じ仕事をしているのはあなたの言う通りです。しかし、いくつかの電話では、fragmenttransactionの代わりにfragmentmanagerを使用している場合、これと同様のいくつかの問題があります。だから私の場合、これは私の問題を解決しました。
RIJO RV 2015

6

私はこの問題に何年も遭遇しています。
インターネットはこれについてのスコア(数十万?
状況を悪化させるために、そしてxkcdの「14標準」コミックの精神の中で、私は自分の答えをリングに投げ込んでいます。
xkcd 14規格

cancelPendingInputEvents()commitAllowingStateLoss()catch (IllegalStateException e)、および同様のソリューションは、すべての凶悪ようです。

うまくいけば、以下は問題を再現して修正する方法を簡単に示しています:

private static final Handler sHandler = new Handler();
private boolean mIsAfterOnSaveInstanceState = true;

@Override
protected void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);
    mIsAfterOnSaveInstanceState = true; // <- To repro, comment out this line
}

@Override
protected void onPostResume()
{
    super.onPostResume();
    mIsAfterOnSaveInstanceState = false;
}

@Override
protected void onResume()
{
    super.onResume();
    sHandler.removeCallbacks(test);
}

@Override
protected void onPause()
{
    super.onPause();
    sHandler.postDelayed(test, 5000);
}

Runnable test = new Runnable()
{
    @Override
    public void run()
    {
        if (mIsAfterOnSaveInstanceState)
        {
            // TODO: Consider saving state so that during or after onPostResume a dialog can be shown with the latest text
            return;
        }

        FragmentManager fm = getSupportFragmentManager();
        DialogFragment dialogFragment = (DialogFragment) fm.findFragmentByTag("foo");
        if (dialogFragment != null)
        {
            dialogFragment.dismiss();
        }

        dialogFragment = GenericPromptSingleButtonDialogFragment.newInstance("title", "message", "button");
        dialogFragment.show(fm, "foo");

        sHandler.postDelayed(test, 5000);
    }
};

1
説明なしで反対票を投じる人々が大好きです。代わりにちょうど投票ダウン、彼らは私の解決策は欠陥がある方法を説明している場合、多分それは良いでしょうか?反対投票者の反対投票に反対票を投じることはできますか?
スウビー、

1
はい、それはSOの問題です。私は提案で毎回この問題を書きますが、彼らは解決したくありません。
CoolMind

2
賛成投票は埋め込まれたXKCDの結果である可能性があると思います。回答は実際にはソーシャルコメントの場所ではありません(どれほどおもしろいとか真実であろうと)。
RestingRobot

5

Activity-KTXの新しいライフサイクルスコープを使用すると、次のコードサンプルと同じくらい簡単です。

lifecycleScope.launchWhenResumed {
   showErrorDialog(...)
}

このメソッドは、onStop()の後に直接呼び出すことができ、onResume()が呼び出されたときにダイアログが正常に表示されます。


3

多くのビューは、クリックハンドラーなどの高レベルのイベントをイベントキューにポストして、遅延して実行します。したがって、問題は「onSaveInstanceState」がアクティビティに対してすでに呼び出されているが、イベントキューに遅延「クリックイベント」が含まれていることです。したがって、このイベントがハンドラーにディスパッチされたとき

at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)

そしてあなたのコードはshowIllegalStateExceptionがスローされます。

最も簡単な解決策は、 onSaveInstanceState

protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // ..... do some work
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            findViewById(android.R.id.content).cancelPendingInputEvents();
        }
}

これで問題が解決することを実際に確認しましたか?
mhsmith

Googleはこれを現在ベータ版のAndroidxライブラリの次のリリースに追加しました(activityおよびfragment)。
mhsmith

1
@mhsmith私はこのソリューションでコードのIllegalStateExceptionの問題を解決したことを覚えています
sim

2

ダイアログフラグメントオブジェクトをグローバルにし、onPause()メソッドでdismissAllowingStateLoss()を呼び出します。

@Override
protected void onPause() {
    super.onPause();

    if (dialogFragment != null) {
        dialogFragment.dismissAllowingStateLoss();
    }
}

フラグメントに値を割り当て、ボタンのクリック時などにshow()を呼び出すことを忘れないでください。


1

公式には言及されていませんが、この問題に何度か直面しました。私の経験では、この問題を引き起こす古いプラットフォームのフラグメントをサポートする互換性ライブラリに何か問題があります。これをテストするには、通常のフラグメントマネージャーAPIを使用します。何も機能しない場合は、ダイアログフラグメントの代わりに通常のダイアログを使用できます。


1
  1. このクラスをプロジェクトに追加します:(android.support.v4.appパッケージにある必要があります)
パッケージandroid.support.v4.app;


/ **
 * 2017年8月16日にギルによって作成されました。
 * /

パブリッククラスStatelessDialogFragment extends DialogFragment {
    / **
     *ダイアログを表示し、既存のトランザクションを使用してフラグメントを追加してから、
     *状態の損失を許容しながらのトランザクション。
* *ほとんどの場合{@link #show(FragmentTransaction、String)}を使用することをお勧めしますが *これは本当に気にしないダイアログ用です。(デバッグ/追跡/広告など) * * @paramトランザクション *フラグメントを追加する既存のトランザクション。 * @paramタグ *このフラグメントのタグ * {@link FragmentTransaction#add(Fragment、String)FragmentTransaction.add}。 * @returnコミットされたトランザクションの識別子を返します * {@link FragmentTransaction#commit()FragmentTransaction.commit()}。 * @see StatelessDialogFragment#showAllowingStateLoss(FragmentManager、String) * / public int showAllowingStateLoss(FragmentTransaction transaction、String tag) { mDismissed = false; mShownByMe = true; transaction.add(this、tag); mViewDestroyed = false; mBackStackId = transaction.commitAllowingStateLoss(); mBackStackIdを返します。 } / ** *ダイアログを表示し、指定されたFragmentManagerにフラグメントを追加します。これは便利です *トランザクションを明示的に作成し、指定されたタグでフラグメントをトランザクションに追加します。 *状態を気にせずにコミットします。これがないではないにトランザクションを追加します *バックスタック。フラグメントを閉じると、新しいトランザクションが実行されてフラグメントが削除されます *アクティビティから。
* *ほとんどの場合、{@ link #show(FragmentManager、String)}を使用することをお勧めしますが、これは *本当に気にしないダイアログの場合。(デバッグ/追跡/広告など) * * * @param manager *このフラグメントが追加されるFragmentManager。 * @paramタグ *このフラグメントのタグ * {@link FragmentTransaction#add(Fragment、String)FragmentTransaction.add}。 * @see StatelessDialogFragment#showAllowingStateLoss(FragmentTransaction、String) * / public void showAllowingStateLoss(FragmentManager manager、String tag) { mDismissed = false; mShownByMe = true; FragmentTransaction ft = manager.beginTransaction(); ft.add(this、tag); ft.commitAllowingStateLoss(); } }
  1. DialogFragmentの代わりにStatelessDialogFragmentを拡張する
  2. showの代わりにshowAllowingStateLossメソッドを使用します

  3. 楽しい ;)


これらすべてのブール型フィールドは何のためのものですか?なぜクラスメンバーとして宣言されていないのですか?
未定義

1
ブールフィールドはDialogFragmentの保護されたメンバーであり、その名前は明らかにそれらが何のためにあるかを示唆しており、DialogFragmentのロジックに干渉しないようにフィールドを更新する必要があります。元のDialogFragmentクラスにはこの関数が存在しますが、パブリックアクセスがないことに注意してください
Gil SH

これらのメンバーは保護されていませんが、内部にありStatelessDialogFragmentます。パッケージの1つに入れるとコンパイルエラーが発生しました。ありがとうございます。まもなく本番環境でテストします。
未定義


1

リフレクションを使用することにより、この問題のエレガントな解決策を見つけました。上記のすべてのソリューションの問題は、フィールドmDismissedおよびmShownByMeがその状態を変更しないことです。

以下のサンプルのように、独自のカスタムボトムシートダイアログフラグメントでメソッド「show」をオーバーライドするだけです(Kotlin)

override fun show(manager: FragmentManager, tag: String?) {
        val mDismissedField = DialogFragment::class.java.getDeclaredField("mDismissed")
        mDismissedField.isAccessible = true
        mDismissedField.setBoolean(this, false)

        val mShownByMeField = DialogFragment::class.java.getDeclaredField("mShownByMe")
        mShownByMeField.isAccessible = true
        mShownByMeField.setBoolean(this, true)

        manager.beginTransaction()
                .add(this, tag)
                .commitAllowingStateLoss()
    }

3
「リフレクションを使用することにより、この問題のエレガントな解決策を見つけました。」エレガントですか?
Mark Buikema

エレガントでスタイリッシュな、シック、スマート、素敵な、優雅な
РомаБогдан

1
それは私のために働いた唯一の解決策です。エレガントだと思います
MBH

0

次の実装はActivity、特にダイアログを表示するために、ライフサイクル中に状態変更を安全に実行する問題を解決するために使用できます。インスタンスの状態がすでに保存されている場合(構成の変更など)、再開された状態になるまでそれらを延期します実行されました。

public abstract class XAppCompatActivity extends AppCompatActivity {

    private String TAG = this.getClass().getSimpleName();

    /** The retained fragment for this activity */
    private ActivityRetainFragment retainFragment;

    /** If true the instance state has been saved and we are going to die... */
    private boolean instanceStateSaved;

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        // get hold of retain Fragment we'll be using
        retainFragment = ActivityRetainFragment.get(this, "Fragment-" + this.getClass().getName());
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();

        // reset instance saved state
        instanceStateSaved = false;

        // execute all the posted tasks
        for (ActivityTask task : retainFragment.tasks) task.exec(this);
        retainFragment.tasks.clear();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        instanceStateSaved = true;
    }

    /**
     * Checks if the activity state has been already saved.
     * After that event we are no longer allowed to commit fragment transactions.
     * @return true if the instance state has been saved
     */
    public boolean isInstanceStateSaved() {
        return instanceStateSaved;
    }

    /**
     * Posts a task to be executed when the activity state has not yet been saved
     * @param task The task to be executed
     * @return true if the task executed immediately, false if it has been queued
     */
    public final boolean post(ActivityTask task)
    {
        // execute it immediately if we have not been saved
        if (!isInstanceStateSaved()) {
            task.exec(this);
            return true;
        }

        // save it for better times
        retainFragment.tasks.add(task);
        return false;
    }

    /** Fragment used to retain activity data among re-instantiations */
    public static class ActivityRetainFragment extends Fragment {

        /**
         * Returns the single instance of this fragment, creating it if necessary
         * @param activity The Activity performing the request
         * @param name The name to be given to the Fragment
         * @return The Fragment
         */
        public static ActivityRetainFragment get(XAppCompatActivity activity, String name) {

            // find the retained fragment on activity restarts
            FragmentManager fm = activity.getSupportFragmentManager();
            ActivityRetainFragment fragment = (ActivityRetainFragment) fm.findFragmentByTag(name);

            // create the fragment and data the first time
            if (fragment == null) {
                // add the fragment
                fragment = new ActivityRetainFragment();
                fm.beginTransaction().add(fragment, name).commit();
            }

            return fragment;
        }

        /** The queued tasks */
        private LinkedList<ActivityTask> tasks = new LinkedList<>();

        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);

            // retain this fragment
            setRetainInstance(true);
        }

    }

    /** A task which needs to be performed by the activity when it is "fully operational" */
    public interface ActivityTask {

        /**
         * Executed this task on the specified activity
         * @param activity The activity
         */
        void exec(XAppCompatActivity activity);
    }
}

次に、次のようなクラスを使用します。

/** AppCompatDialogFragment implementing additional compatibility checks */
public abstract class XAppCompatDialogFragment extends AppCompatDialogFragment {

    /**
     * Shows this dialog as soon as possible
     * @param activity The activity to which this dialog belongs to
     * @param tag The dialog fragment tag
     * @return true if the dialog has been shown immediately, false if the activity state has been saved
     *         and it is not possible to show it immediately
     */
    public boolean showRequest(XAppCompatActivity activity, final String tag) {
        return showRequest(activity, tag, null);
    }

    /**
     * Shows this dialog as soon as possible
     * @param activity The activity to which this dialog belongs to
     * @param tag The dialog fragment tag
     * @param args The dialog arguments
     * @return true if the dialog has been shown immediately, false if the activity state has been saved
     *         and it is not possible to show it immediately
     */
    public boolean showRequest(XAppCompatActivity activity, final String tag, final Bundle args)
    {
        return activity.post(new XAppCompatActivity.ActivityTask() {
            @Override
            public void exec(XAppCompatActivity activity) {
                if (args!= null) setArguments(args);
                show(activity.getSupportFragmentManager(), tag);
            }
        });
    }

    /**
     * Dismiss this dialog as soon as possible
     * @return true if the dialog has been dismissed immediately, false if the activity state has been saved
     *         and it is not possible to dismissed it immediately
     */
    public boolean dismissRequest()
    {
        return dismissRequest(null);
    }

    /**
     * Dismiss this dialog as soon as possible
     * @param runnable Actions to be performed before dialog dismissal
     * @return true if the dialog has been dismissed immediately, false if the activity state has been saved
     *         and it is not possible to dismissed it immediately
     */
    public boolean dismissRequest(final Runnable runnable)
    {
        // workaround as in rare cases the activity could be null
        XAppCompatActivity activity = (XAppCompatActivity)getActivity();
        if (activity == null) return false;

        // post the dialog dismissal
        return activity.post(new XAppCompatActivity.ActivityTask() {
            @Override
            public void exec(XAppCompatActivity activity) {
                if (runnable != null) runnable.run();
                dismiss();
            }
        });
    }
}

アプリの状態を気にすることなくダイアログを安全に表示できます。

public class TestDialog extends XAppCompatDialogFragment {

    private final static String TEST_DIALOG = "TEST_DIALOG";

    public static void show(XAppCompatActivity activity) {
        new TestDialog().showRequest(activity, TEST_DIALOG);
    }

    public TestDialog() {}

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        return new AlertDialog.Builder(getActivity(), R.style.DialogFragmentTheme /* or null as you prefer */)
                .setTitle(R.string.title)
                // set all the other parameters you need, e.g. Message, Icon, etc.
                ).create();
    }
}

その後、TestDialog.show(this)から呼び出しますXAppCompatActivity

あなたはパラメータを指定して、より汎用的なダイアログクラスを作成する場合は、それらを保存することができますBundle内の引数を持つshow()メソッドとでそれらを取得getArguments()onCreateDialog()

アプローチ全体は少し複雑に見えるかもしれませんが、アクティビティとダイアログの2つの基本クラスを作成すると、非常に使いやすく、完全に機能します。Fragment同じ問題によって影響を受ける可能性がある他のベースの操作に使用できます。


0

このエラーは、キーが押されたイベントやonclickイベントなどの入力イベントonSaveInstanceStateが呼び出された後に配信されるために発生しているようです。

解決策はonSaveInstanceState、アクティビティでオーバーライドし、保留中のイベントをキャンセルすることです。

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        final View rootView = findViewById(android.R.id.content);
        if (rootView != null) {
            rootView.cancelPendingInputEvents();
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.