Після відображення діалогового вікна я отримую "Не можу виконати цю дію після onSaveInstanceState"


121

Деякі користувачі звітують, якщо вони використовують швидку дію на панелі сповіщень, вони закривають силу.

Я показую швидку дію в повідомленні, яке викликає клас "TestDialog" . У класі 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/…
bogdan

4
Чи відображається ваша активність при запуску цього діалогового вікна? Це здається, що це може бути спричинене тим, що ваша програма намагається відобразити діалогове вікно, приєднане до дії, яке було призупинено / зупинено.
Кай

stackoverflow.com/questions/7575921/… Ви спробували це, я впевнений.
Оріон

Відповіді:


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;

Це може призвести до несподіваних результатів у деяких випадках. Краще використовувати commitAllowingStateLoss (), а не виконувати ()


3
Але чому це питання виникає? Чи добре ігнорувати помилку? Що відбувається, коли ти робиш? Зрештою, при натисканні це означає, що активність живе і добре ... У будь-якому разі, я про це повідомляв тут, тому що вважаю це помилкою: code.google.com/p/android/isissue/detail?id= 207269
андроїд розробник

1
Чи можете ви, будь ласка, зірочкою та / або прокоментувати там?
андроїд розробник

2
краще зателефонувати super.show (менеджер, тег) всередині пробної пропозиції. Прапори, що належать DialogFragment, можуть залишатися безпечними таким чином
Shayan_Aryan

20
У цей момент ви можете викликати commitAllowingStateLoss () замість commit (). Виняток не піднімався.
ARLabs

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 в іншому фрагменті?
андроїд розробник

Нашим рішенням є створення активності та фрагмента базового класу та делегування onResumeFragments до фрагменту (ми створюємо onResumeFragments в базовому класі фрагменту). Це не приємне рішення, але воно працює. Якщо у вас є якесь краще рішення, будь ласка, повідомте мене :)
Pongpat

Ну, я вважав, що показ діалогового вікна в "onStart" повинен працювати добре, оскільки фрагмент, безумовно, показується, але я все ще бачу деякі звіти про аварійне завершення роботи. Мені доручили спробувати поставити це на "onResume" замість цього. Про альтернативи я бачив таке: twigstechtips.blogspot.co.il/2014/01/… , але це досить дивно.
андроїд розробник

Я думаю, причина, що twigstechtips.blogspot.co.il/2014/01/… працює тому, що він запускає нову нитку, а значить, і весь код життєвого циклу, тобто onStart, onResume тощо, викликаний перед запуском коду runOnUiThread. Це означає, що стан вже відновлено до виклику runOnUiThread.
Понгпат

2
Я використовую один дзвінок для публікації (можна виконати). Щодо getFragmentManager, це залежить. Якщо ви хочете поділитися цим діалогом з іншою діяльністю, вам слід скористатися getFragmentManager, однак, якщо це діалогове вікно існує лише з фрагментом getChildFragmentManager, здається, кращий вибір.
Понгпат

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об'єкт. Ось приклад у Котліні:

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

        }

    }

1
Так що розробники не повинні успадковуватися від DialogFragmentви могли б змінити це функція розширення Котлин з наступною підписом: fun DialogFragment.showAllowingStateLoss(fragmentManager: FragmentManager, tag: String). Крім того, пробний лов не потрібен, оскільки ви викликаєте commitAllowingStateLoss()метод, а не 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).
андроїд розробник

Ти врятував мені день. Дякую, Френк.
Cüneyt

7

Я роблю цю проблему роками.
Інтернети переповнені десятками (сотні - тисячі?) Дискусій з цього приводу, і плутанина та дезінформація в них здається достатньою.
Щоб погіршити ситуацію, і в дусі коміксу 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);
    }
};

2
Я люблю людей, які голосують без пояснення. Замість того, щоб просто проголосувати, можливо, було б краще, якби вони пояснили, наскільки моє рішення хибно? Чи можу я проголосувати противником голосу "проти"?
swooby

1
Так, це проблема СО, я пишу цю проблему кожен раз у пропозиціях, але вони не хочуть вирішувати.
CoolMind

2
Я думаю, що голосування можуть бути результатом вбудованого XKCD, відповіді насправді не є місцем для соціальних коментарів (як би не було смішно та / або правдиво).
RestingRobot

6

будь ласка, спробуйте використовувати FragmentTransaction замість FragmentManager. Я думаю, що наведений нижче код вирішить вашу проблему. Якщо ні, будь ласка, дайте мені знати.

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

Редагувати:

Фрагмент транзакції

Перевірте це посилання. Я думаю, це вирішить ваші запити.


4
Будь-яке пояснення, чому використання FragmentTransaction виправляє проблему, було б чудово.
Hemanshu

3
Діалог # show (FragmentManager, тег) робить те саме. Це не рішення.
Вільям

3
Ця відповідь не є рішенням. DialogFragment # show (ft) та show (fm) роблять точно те саме.
danijoo

@danijoo Ви маєте рацію, що обидва працюють однаково. Але в кількох телефонах є певна проблема, подібна до цієї, якщо ви використовуєте fragmentmanager замість fragmenttransaction. Тож у моєму випадку це вирішило моє питання.
RIJO RV

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

Зробіть об'єкт фрагмента діалогу глобальним і зателефонуйте dismissAllowingStateLoss () методом onPause ()

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

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

Не забудьте призначити значення у фрагменті та зателефонувати show () при натисканні кнопки чи де завгодно.


1

Хоча це офіційно ніде не згадується, але я стикався з цією проблемою кілька разів. На мій досвід, у бібліотеці сумісності, що підтримує фрагменти на старих платформах, щось не так, що спричиняє цю проблему. Ви використовуєте тест, використовуючи звичайний API менеджера фрагментів. Якщо нічого не працює, замість фрагмента діалогу можна використовувати звичайний діалог.


1
  1. Додайте цей клас до свого проекту: (має бути в пакеті android.support.v4.app )
пакет android.support.v4.app;


/ **
 * Створено Гілем 16.08.2017.
 * /

public class StatelessDialogFragment розширює DialogFragment {
    / **
     * Відобразить діалогове вікно, додавши фрагмент, використовуючи існуючу транзакцію, а потім здійснивши
     * транзакція, дозволяючи втрати держави.
* * Я рекомендую вам використовувати {@link #show (FragmentTransaction, String)} більшу частину часу, але * це для діалогів, про які ви, мабуть, не переймаєтесь. (Налагодження / відстеження / реклами тощо) * * транзакція @param * Існуюча транзакція, в яку потрібно додати фрагмент. * тег @param * Тег для цього фрагмента, згідно з пер * {@link FragmentTransaction # add (Фрагмент, рядок) FragmentTransaction.add}. * @return Повертає ідентифікатор вчиненої транзакції відповідно до в.ч. * {@link FragmentTransaction # commit () FragmentTransaction.commit ()}. * @ see StatelessDialogFragment # showAllowingStateLoss (FragmentManager, String) * / public int showAllowingStateLoss (транзакція FragmentTransaction, тег String) { mDismissed = хибний; mShownByMe = вірно; action.add (це, тег); mViewDestroyed = помилково; mBackStackId =action.commitAllowingStateLoss (); повернути mBackStackId; } / ** * Відобразить діалогове вікно, додавши фрагмент до даного FragmentManager. Це зручність * для явного створення транзакції, додавання до неї фрагмента із заданим тегом та * вчинення цього, не піклуючись про державу. Це не так додає транзакції до * зворотний стек. Коли фрагмент буде відхилено, буде виконано нову транзакцію для його видалення * від діяльності.
* * Я рекомендую вам використовувати {@link #show (FragmentManager, String)} більшу частину часу, але це * для діалогових діалогів, які вам напевно не цікаві. (Налагодження / відстеження / реклами тощо) * * * @param manager * FragmentManager до цього фрагменту буде додано. * тег @param * Тег для цього фрагмента, згідно з пер * {@link FragmentTransaction # add (Фрагмент, рядок) FragmentTransaction.add}. * @see StatelessDialogFragment # showAllowingStateLoss (FragmentTransaction, String) * / публічне недійсне шоуAllowingStateLoss (менеджер FragmentManager, тег String) { mDismissed = хибний; mShownByMe = вірно; FragmentTransaction ft = manager.beginTransaction (); ft.add (це, тег); ft.commitAllowingStateLoss (); } }
  1. Продовжити StatelessDialogFragment замість DialogFragment
  2. Використовуйте метод showAllowingStateLoss замість show

  3. Насолоджуйтесь;)


Для чого ці всі булеві поля? Чому вони не оголошені членами класу?
визначено

1
Булеві поля є захищеними членами DialogFragment, їх імена, очевидно, підказують, для чого вони потрібні, і нам потрібно оновити їх, щоб не втручатися в логіку DialogFragment. Зауважте, що в оригінальному класі DialogFragment ці функції існують, але без загальнодоступного доступу
Gil SH

О, ці учасники не захищені, вони є внутрішніми. Я отримував помилки компіляції, коли я поміщав StatelessDialogFragmentвсередину одного зі своїх пакунків.
визначено

1

використовувати цей код

FragmentTransaction ft = fm.beginTransaction();
        ft.add(yourFragment, "fragment_tag");
        ft.commitAllowingStateLoss();

замість

yourFragment.show(fm, "fragment_tag");

1

Я знайшов елегантне рішення цієї проблеми за допомогою рефлексії. Проблема всіх вищезазначених рішень полягає в тому, що поля mDismissed та mShownByMe не змінюють свого стану.

Просто замініть метод "показати" у вашому власному фрагменті діалогового вікна нижнього аркуша, як зразок нижче (Котлін)

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

4
"Я знайшов елегантне рішення цієї проблеми за допомогою рефлексії". як це елегантно?
Марк Буйкема

елегантний, стильний, шикарний, розумний, приємний, витончений
Рома Богдан

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.

Якщо ви хочете створити більш загальний діалоговий клас з параметрами, ви можете зберегти їх у a Bundleз аргументами show()методу та отримати їх getArguments()у onCreateDialog().

Весь підхід може здатися трохи складним, але як тільки ви створили два базових класи для діяльності та діалогів, він досить простий у використанні та ідеально працює. Він може бути використаний для інших Fragmentоперацій на основі, які можуть бути порушені тією ж проблемою.


0

Ця помилка, як видається, виникає через те, що події введення (наприклад, події вниз або натискання клавіші) надходять після виклику 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();
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.