Android에서 화면 회전시 대화 상자 닫기 방지


94

활동이 다시 시작될 때 경고 빌더로 빌드 된 대화 상자가 닫히지 않도록 방지하려고합니다.

onConfigurationChanged 메서드를 오버로드하면 성공적으로이 작업을 수행하고 레이아웃을 올바른 방향으로 재설정 할 수 있지만 edittext의 고정 텍스트 기능이 손실됩니다. 그래서 대화 문제를 해결하면서이 edittext 문제를 만들었습니다.

편집 텍스트에서 문자열을 저장하고 onCofiguration 변경에서 다시 할당하면 여전히 회전 전에 입력 한 값이 아닌 초기 값으로 기본 설정되는 것처럼 보입니다. 강제로 무효화하더라도 업데이트하는 것 같습니다.

대화 문제 나 edittext 문제를 해결해야합니다.

도와 주셔서 감사합니다.


1
편집 된 EditText의 내용을 어떻게 저장 / 복원합니까? 코드를 보여줄 수 있습니까?
Peter Knego 2011 년

그 문제를 알아 냈고, 레이아웃을 재설정 한 후 이드가 다시보기를 잊고 있었다.
draksia 2011 년

답변:


134

요즘이 문제를 피하는 가장 좋은 방법은 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+

이 답변은 다음 세 가지 질문 (및 답변)을 설명하는 데 도움이됩니다.


내 앱에 .show ()를 호출하는 버튼이 있습니다. 표시 / 해제 된 경고 대화 상자의 상태를 기억해야합니다. .show ()를 호출하지 않고 대화 상자를 유지하는 방법이 있습니까?
Alpha Huang

1
onAttach지금은 더 이상 사용되지 않는다고 말합니다 . 대신 무엇을해야합니까?
farahm

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

3
이 코드가 나쁘다고 생각합니다. doLogout ()에는 활동을 포함하거나 포함하는 컨텍스트에 대한 참조가 있습니다. 활동을 파괴 할 수 없으므로 메모리 누수가 발생할 수 있습니다. 정적 컨텍스트에서 AlertDialog를 사용할 가능성을 찾고 있었지만 이제는 불가능하다고 확신합니다. 그 결과는 내가 생각하기에 쓰레기가 될 수 있습니다.
놀라운 1

2
작동하는 것처럼 보입니다. 대화 상자는 열린 상태로 유지되지만 새로 생성 된 활동 또는 조각에 연결되어 있지 않습니다 (각 방향 변경시 새로 생성됨). 따라서 Context대화 상자 내부 버튼 이 필요한 작업을 수행 할 수 없습니다 OnClickListener.
Udo Klimaschewski

2
이 코드는 작동하지만 전혀 권장하지 않습니다. 활동 참조가 누출되므로 대화가 지속될 수 있습니다. 이것은 메모리 누수로 이어질 매우 나쁜 습관입니다.
Hexise

4

방향 변경시 레이아웃을 변경하는 경우 android:configChanges="orientation" 어쨌든 뷰를 다시 생성하기 때문에 매니페스트에 입니다.

다음 방법을 사용하여 활동의 현재 상태 (입력 한 텍스트, 표시된 대화 상자, 표시된 데이터 등)를 저장합니다.

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

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

이렇게하면 활동이 다시 onCreate를 거치고 나중에 EditText 값을 다시 설정할 수있는 onRestoreInstanceState 메서드를 호출합니다.

더 복잡한 객체를 저장하려면 다음을 사용할 수 있습니다.

@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은 완전히 다릅니다. Fragments 용이며 인스턴스가 유지된다는 보장은 없습니다.
Miha_x64

3

AndroidManifest.xml의 활동 요소와 함께 android : configChanges = "orientation"을 추가하기 만하면됩니다.

예:

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

이로 인해 일부 상황에서 대화 상자가 잘못 표시 될 수 있습니다.
Sam

1
android : configChanges = "orientation | screenSize"참고 : 애플리케이션이 Android 3.2 (API 레벨 13) 이상을 대상으로하는 경우 'screenSize'구성도 선언해야합니다. 기기가 세로 방향과 가로 방향 사이를 전환 할 때도 변경되기 때문입니다.
tsig

1

매우 쉬운 방법은 메서드에서 대화 상자를 만드는 것입니다 onCreateDialog()(아래 참고 참조). 을 통해 보여줍니다 showDialog(). 이 방법은, 안드로이드는 당신을위한 회전을 처리하고 당신이 호출 할 필요가 없습니다 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 2012

나는 그것을 할 수 있었다. 그러나 문제는, onCreateDialog ()는 이제 더 이상 사용되지
않는다는 것입니다

확인! 대부분의 Android 기기는 여전히 2.X 버전에서 작동하므로 어쨌든 사용할 수 있습니다! Android 플랫폼 버전 사용
Caumons

그래도 onCreateDialog가 아닌 다른 옵션은 무엇입니까?
neteinstein 2012

1
예를 들어 빌더 클래스를 사용할 수 있습니다 AlertDialog.Builder. 당신은 내부를 사용하는 경우 onCreateDialog()사용하는 대신, show()결과를 반환합니다 create(). 그렇지 않으면 show()반환 된 AlertDialog를 호출 하여 활동의 속성에 저장하고 onPause() dismiss()WindowLeak을 방지하기 위해 표시되는 경우 여기 에 저장 합니다. 도움이 되었기를 바랍니다.
Caumons 2012

1

이 질문은 오래 전에 답변되었습니다.

그러나이는 비 해키간단한 자신을 위해 솔루션 I 사용은.

이 도우미 클래스 를 직접 만들었 으므로 응용 프로그램에서도 사용할 수 있습니다.

사용법은 다음과 같습니다.

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를 사용하는 것입니다.

다음은 하나의 프래그먼트 (또는 작은 리팩토링이있는 활동) 내에서 다른 대화 상자가 닫히는 것을 방지하는 데 도움이되는 래퍼 클래스의 내 솔루션입니다. 또한 어떤 이유로 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);
    }
}

활동에 관해서는 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 년 후에도 여전히 필요하다는 사실은 놀랍습니다.



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.