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


94

アクティビティが再起動されたときにAlert Builderで作成されたダイアログが閉じられないようにしています。

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

edittextから文字列を保存し、onCofigurationの変更でそれらを再割り当てしても、ローテーションの前に入力されたものではなく、デフォルトで初期値になっているようです。無効化を強制したとしても、それらは更新されるようです。

私は本当にダイアログの問題かエディットテキストの問題のどちらかを解決する必要があります。

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


1
編集したEditTextの内容をどのように保存/復元しますか?コードを見せていただけますか?
Peter Knego、2011

私はそれで問題を理解しました、私はレイアウトをリセットした後にIdによってビューを再び取得するのを忘れていました。
ドラクシア

回答:


134

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

拡張する新しいクラスを作成しますDialogFragmentonCreateDialog古い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つの質問(およびその回答)を説明するのに役立ちます。


私のアプリには.show()を呼び出すボタンがあり、アラートダイアログの状態を表示/非表示にしておく必要があります。.show()を呼び出さずにダイアログを保持する方法はありますか?
Alpha Huang

1
現在onAttachは非推奨となっています。代わりに何をすべきですか?
ファラーム

3
@faraz_ahmed_kamran、とを使用する必要がonAttach(Context context)ありandroid.support.v4.app.DialogFragmentます。このonAttachメソッドは、パラメータとしてではcontextなく、activity現在使用しています。
Stan Mots

おそらくおそらく必要はYesNoListenerありません。この回答を参照してください。
Mygod

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

1
私のコードが役に立たないと思う人は、クリックする前に試してください:-)
Chung IW

6
機能しますが、方法はわかりませんが、機能します!クリーンでシンプルで抽象的なソリューション、ありがとうございます。
nmvictor 2015年

3
このコードは悪いと思います。doLogout()には、アクティビティである/を含むコンテキストへの参照があります。アクティビティは破棄できず、メモリリークが発生する可能性があります。静的コンテキストからAlertDialogを使用する可能性を探していましたが、今では不可能だと確信しています。結果は、私が思うにゴミになるだけです。
驚異的な1

2
それはうまくいくように見えます。ダイアログは開いたままですが、新しく作成されたアクティビティまたはフラグメントへの接続はありません(向きが変わるたびに新しく作成されます)。したがってContext、ダイアログボタンの内側を必要とすることはできませんOnClickListener
Udo Klimaschewski、2018年

2
このコードは機能しますが、まったくお勧めしません。アクティビティ参照がリークするため、ダイアログが持続する可能性があります。これはメモリリークにつながる非常に悪い習慣です。
ヘキシス2018

4

方向変更時にレイアウトを変更する場合、私は入れません 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
OnRetainNonConfigurationInstance():ドキュメントが言うように廃止されdeveloper.android.com/reference/android/app/...が setRetainInstance(boolean retain)代わりに使用する必要があります:developer.android.com/reference/android/app/...
ForceMagic

@ForceMagic setRetainInstanceは完全に異なります。これはフラグメント用であり、インスタンスが保持されることを保証するものではありません。
Miha_x64 2017

3

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

例:

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

これにより、状況によってはダイアログが正しく表示されないことがあります。
サム

1
android:configChanges = "orientation | screenSize"注:アプリケーションがAndroid 3.2(APIレベル13)以上をターゲットにしている場合は、「screenSize」構成も宣言する必要があります。これは、デバイスが縦向きと横向きに切り替わると変更されるためです。
tsig 2018年

1

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

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

見る AndroidのドキュメントshowDialog()をしてください。それが誰かを助けることを願っています!

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


どの場合にコードが必要ですか?onCreateDialog()またはビルダーで表示してshow()を呼び出しますか?
Caumons

私はそれをなんとか
成し遂げ

OK!ほとんどのAndroidデバイスは引き続き2.Xバージョンで動作するので、とにかくそれを使用できます。見ていAndroidプラットフォームのバージョンの利用
Caumons

それでも、onCreateDialogでない場合、他のオプションは何ですか?
neteinstein

1
たとえば、ビルダークラスを使用できますAlertDialog.Builder。内部onCreateDialog()で使用する場合、使用する代わりにshow()の結果を返しcreate()ます。それ以外の場合はshow()、返されたAlertDialogを呼び出してアクティビティの属性に格納し、WindowLeak onPause() dismiss()を回避するために表示する場合はその中に格納します。それが役に立てば幸い!
Caumons 2012

1

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

しかし、これは非ハックシンプルです私が自分で使用するソリューションです。

私はこのヘルパークラスをやった自分のために、あなたはあまりにも、あなたのアプリケーションでそれを使用することができます。

使い方は:

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

ダイアログのonSave / onRestoreメソッドをアクティビティのonSave / onRestoreメソッドと組み合わせて、ダイアログの状態を維持できます。

注:このメソッドは、警告メッセージの表示など、「単純な」ダイアログで機能します。ダイアログに埋め込まれた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

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

これは、1つのフラグメント(または小さなリファクタリングを使用するアクティビティ)内でさまざまなダイアログが閉じられるのを防ぐのに役立つラッパークラスのマイソリューションです。また、なんらかの理由で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);
        }
    }
}

あなたは私のブログの完全な記事を読むことができますダイアログが却下されるのを防ぐ方法は?そして、ソースコードをいじってください


1

これは、「すべてを正しく行って」使用した場合でも、まだ問題であるようです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年が経過しても、これはまだ必要です。


stackoverflow.com/questions/14657490/…も参照してください。
CoolMind

0

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


-3

使うだけ

ConfigurationChanges = Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize

アプリは回転と画面サイズの処理方法を知っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.