web-dev-qa-db-ja.com

Androidで画面の回転時にダイアログが消えないようにする

アクティビティの再起動時に、アラートビルダーで作成されたダイアログが消えないようにしようとしています。

OnConfigurationChangedメソッドをオーバーロードすると、これを正常に実行してレイアウトを正しい方向にリセットできますが、edittextのスティッキーテキスト機能が失われます。したがって、ダイアログの問題を解決する際に、この編集テキストの問題を作成しました。

編集テキストから文字列を保存し、onCofigurationの変更で文字列を再割り当てしても、回転前に入力された値ではなく、初期値がデフォルトのままであるように見えます。無効化を強制しても、それらは更新されるようです。

ダイアログの問題か、テキストの編集の問題を解決する必要があります。

助けてくれてありがとう。

90
draksia

最近この問題を回避する最善の方法は、 DialogFragment を使用することです。

DialogFragment を拡張する新しいクラスを作成します。 onCreateDialog をオーバーライドして、古い Dialog または AlertDialog を返します。

次に、 DialogFragment.show(fragmentManager, tag) で表示できます。

Listener の例を次に示します。

public class MyDialogFragment extends DialogFragment {

    public interface YesNoListener {
        void onYes();

        void onNo();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (!(activity instanceof YesNoListener)) {
            throw new ClassCastException(activity.toString() + " must implement YesNoListener");
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
                .setTitle(R.string.dialog_my_title)
                .setMessage(R.string.dialog_my_message)
                .setPositiveButton(Android.R.string.yes, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ((YesNoListener) getActivity()).onYes();
                    }
                })
                .setNegativeButton(Android.R.string.no, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ((YesNoListener) getActivity()).onNo();
                    }
                })
                .create();
    }
}

そして、あなたが呼び出すアクティビティで:

new MyDialogFragment().show(getSupportFragmentManager(), "tag"); // or getFragmentManager() in API 11+

この回答は、これらの他の3つの質問(およびその回答)の説明に役立ちます。

124
Brais Gabin
// Prevent dialog dismiss when orientation changes
private static void doKeepDialog(Dialog dialog){
    WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
    lp.copyFrom(dialog.getWindow().getAttributes());
    lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
    lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
    dialog.getWindow().setAttributes(lp);
}
public static void doLogout(final Context context){     
        final AlertDialog dialog = new AlertDialog.Builder(context)
        .setIcon(Android.R.drawable.ic_dialog_alert)
        .setTitle(R.string.titlelogout)
        .setMessage(R.string.logoutconfirm)
        .setPositiveButton("Yes", new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ...   
            }

        })
        .setNegativeButton("No", null)      
        .show();    

        doKeepDialog(dialog);
    }
44
Chung IW

AndroidManifest.xmlのアクティビティ要素にAndroid:configChanges = "orientation"を追加するだけです

例:

<activity
            Android:name=".YourActivity"
            Android:configChanges="orientation"
            Android:label="@string/app_name"></activity>
4
SAAM

向きの変更時にレイアウトを変更する場合は、とにかくビューを再作成するため、マニフェストに_Android:configChanges="orientation"_を入れません。

次の方法を使用して、アクティビティの現在の状態(入力されたテキスト、表示されたダイアログ、表示されたデータなど)を保存します。

_@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
}
_

そのようにして、アクティビティは再びonCreateを通過し、その後、onRestoreInstanceStateメソッドを呼び出します。このメソッドでは、EditText値を再度設定できます。

より複雑なオブジェクトを保存する場合は、使用できます

_@Override
public Object onRetainNonConfigurationInstance() {
}
_

ここでは、任意のオブジェクトを保存でき、onCreateでgetLastNonConfigurationInstance();を呼び出すだけでオブジェクトを取得できます。

4
Maria Neumayer

「すべてを正しく行う」ことやDialogFragmentなどを使用する場合でも、これはまだ問題のようです。

Google Issue Tracker にはスレッドがあります。これは、メッセージキューに残っている古い破棄メッセージが原因であると主張しています。提供される回避策は非常に簡単です。

    @Override
    public void onDestroyView() {
        /* Bugfix: https://issuetracker.google.com/issues/36929400 */
        if (getDialog() != null && getRetainInstance())
            getDialog().setDismissMessage(null);

        super.onDestroyView();
    }

この問題が最初に報告されてから7年が経過しても、これが必要であることは信じられません。

1
Magnus W

DialogのonSave/onRestoreメソッドをActivityのonSave/onRestoreメソッドと組み合わせて、Dialogの状態を保持できます。

注:このメソッドは、アラートメッセージの表示など、これらの「単純な」ダイアログに対して機能します。ダイアログに埋め込まれたWebViewのコンテンツは再現しません。複雑なダイアログが回転中に消えないようにしたい場合は、Chung IWの方法を試してください。

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
     super.onRestoreInstanceState(savedInstanceState);
     myDialog.onRestoreInstanceState(savedInstanceState.getBundle("DIALOG"));
     // Put your codes to retrieve the EditText contents and 
     // assign them to the EditText here.
}

@Override
protected void onSaveInstanceState(Bundle outState) {
     super.onSaveInstanceState(outState);
     // Put your codes to save the EditText contents and put them 
     // to the outState Bundle here.
     outState.putBundle("DIALOG", myDialog.onSaveInstanceState());
}
1
hackjutsu

間違いなく、最良のアプローチはDialogFragmentを使用することです。

ラッパークラスの私のソリューションは、1つのFragment(または小さなリファクタリングを伴うActivity)内で異なるダイアログが消えないようにするのに役立ちます。また、何らかの理由で、アクション、外観、またはその他の点でコード間にわずかな違いがあるAlertDialogsが散在している場合、大規模なコードのリファクタリングを回避するのに役立ちます。

_public class DialogWrapper extends DialogFragment {
    private static final String ARG_DIALOG_ID = "ARG_DIALOG_ID";

    private int mDialogId;

    /**
     * Display dialog fragment.
     * @param invoker  The fragment which will serve as {@link AlertDialog} alert dialog provider
     * @param dialogId The ID of dialog that should be shown
     */
    public static <T extends Fragment & DialogProvider> void show(T invoker, int dialogId) {
        Bundle args = new Bundle();
        args.putInt(ARG_DIALOG_ID, dialogId);
        DialogWrapper dialogWrapper = new DialogWrapper();
        dialogWrapper.setArguments(args);
        dialogWrapper.setTargetFragment(invoker, 0);
        dialogWrapper.show(invoker.getActivity().getSupportFragmentManager(), null);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDialogId = getArguments().getInt(ARG_DIALOG_ID);
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return getDialogProvider().getDialog(mDialogId);
    }

    private DialogProvider getDialogProvider() {
        return (DialogProvider) getTargetFragment();
    }

    public interface DialogProvider {
        Dialog getDialog(int dialogId);
    }
}
_

Activityに関しては、getContext()内でonCreateDialog()を呼び出し、DialogProviderインターフェイスにキャストして、mDialogIdによって特定のダイアログを要求できます。ターゲットフラグメントを処理するためのすべてのロジックを削除する必要があります。

フラグメントからの使用:

_public class MainFragment extends Fragment implements DialogWrapper.DialogProvider {
    private static final int ID_CONFIRMATION_DIALOG = 0;

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        Button btnHello = (Button) view.findViewById(R.id.btnConfirm);
        btnHello.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DialogWrapper.show(MainFragment.this, ID_CONFIRMATION_DIALOG);
            }
        });
    }

    @Override
    public Dialog getDialog(int dialogId) {
        switch (dialogId) {
            case ID_CONFIRMATION_DIALOG:
                return createConfirmationDialog(); //Your AlertDialog
            default:
                throw new IllegalArgumentException("Unknown dialog id: " + dialogId);
        }
    }
}
_

あなたは私のブログで完全な記事を読むことができます Dialogが消えないようにするには? そして source code で遊んでください。

1

非常に簡単なアプローチは、メソッドonCreateDialog()からダイアログを作成することです(以下の注を参照)。 showDialog()で表示します。この方法では、Androidは回転を処理します。WindowLeakを回避するためにdismiss()onPause()を呼び出す必要はありません。ダイアログを復元します。ドキュメントから:

このアクティビティが管理するダイアログを表示します。 onCreateDialog(int、Bundle)の呼び出しは、指定されたIDに対して最初に呼び出されたときに同じIDで行われます。その後、ダイアログは自動的に保存および復元されます。

詳細については、 Android docs showDialog() を参照してください。それが誰かを助けることを願っています!

注:AlertDialog.Builderを使用する場合、show()からonCreateDialog()を呼び出さないで、create()代わりに。 ProgressDialogを使用する場合は、オブジェクトを作成し、必要なパラメーターを設定して、それを返します。結論として、show()内のonCreateDialog()は問題を引き起こし、de Dialogインスタンスを作成してそれを返します。これは動作するはずです! (onCreate()からshowDialog()を使用して実際にダイアログを表示しない問題を経験しましたが、onResume()またはリスナーコールバックで使用するとうまく機能します)。

1
Caumons

この質問はずっと前に回答されました。

まだこれはnon-hackysimpleのソリューションです。

私は このヘルパークラス を使用したので、アプリケーションでも使用できます。

使用法は次のとおりです。

PersistentDialogFragment.newInstance(
        getBaseContext(),
        RC_REQUEST_CODE,
        R.string.message_text,
        R.string.positive_btn_text,
        R.string.negative_btn_text)
        .show(getSupportFragmentManager(), PersistentDialogFragment.TAG);

または

 PersistentDialogFragment.newInstance(
        getBaseContext(),
        RC_EXPLAIN_LOCATION,
        "Dialog title", 
        "Dialog Message", 
        "Positive Button", 
        "Negative Button", 
        false)
    .show(getSupportFragmentManager(), PersistentDialogFragment.TAG);





public class ExampleActivity extends Activity implements PersistentDialogListener{

        @Override
        void onDialogPositiveClicked(int requestCode) {
                switch(requestCode) {
                  case RC_REQUEST_CODE:
                  break;
                }
        }

        @Override
        void onDialogNegativeClicked(int requestCode) {
                switch(requestCode) {
                  case RC_REQUEST_CODE:
                  break;
                }          
        }
}
1
Ioane Sharvadze

同様の問題がありました。画面の向きが変わると、ユーザーがダイアログを閉じなかったにもかかわらず、ダイアログのonDismissリスナーが呼び出されました。代わりにonCancelリスナーを使用してこの問題を回避することができました。これは、ユーザーが戻るボタンを押したときと、ユーザーがダイアログの外側をタッチしたときにトリガーしました。

0
Sam