在Android中防止屏幕旋转时取消对话框

在Android中防止屏幕旋转时取消对话框,android,dialog,android-edittext,onconfigurationchanged,Android,Dialog,Android Edittext,Onconfigurationchanged,我试图防止在活动重新启动时关闭使用Alert builder生成的对话框 如果我重载onConfigurationChanged方法,我可以成功地执行此操作并将布局重置为正确的方向,但我会丢失edittext的粘滞文本功能。因此,在解决对话问题时,我创建了这个edittext问题 如果我保存edittext中的字符串并在OnConfiguration更改中重新分配它们,它们似乎仍然默认为初始值,而不是旋转前输入的值。即使我强制执行一个invalidate,它似乎也会更新它们 我真的需要解决对话问

我试图防止在活动重新启动时关闭使用Alert builder生成的对话框

如果我重载onConfigurationChanged方法,我可以成功地执行此操作并将布局重置为正确的方向,但我会丢失edittext的粘滞文本功能。因此,在解决对话问题时,我创建了这个edittext问题

如果我保存edittext中的字符串并在OnConfiguration更改中重新分配它们,它们似乎仍然默认为初始值,而不是旋转前输入的值。即使我强制执行一个invalidate,它似乎也会更新它们

我真的需要解决对话问题或编辑文本问题


感谢您的帮助。

如果您在更改方向时更改布局,我不会将
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()
获取对象。

一个非常简单的方法是从方法
onCreateDialog()
创建对话框(请参见下面的注释)。您可以通过
showDialog()
显示它们。通过这种方式,Android为您处理旋转,您不必在
onPause()
中调用
dismise()
,以避免窗口泄漏,然后您也不必恢复对话框。从文档中:

显示由此活动管理的对话框。对于给定id首次调用onCreateDialog(int,Bundle)时,将使用相同的id进行调用。此后,对话框将自动保存和恢复

有关更多信息,请参阅。希望它能帮助别人


注意:如果使用AlertDialog.Builder,不要从
onCreateDialog()
调用
show()
,而是调用
create()
。如果使用ProgressDialog,只需创建对象,设置所需参数并返回即可。总之,
show()
内部的
onCreateDialog()
会导致问题,只需创建de Dialog实例并返回它即可。这应该管用!(我在使用onCreate()中的showDialog()时遇到过问题-实际上没有显示对话框-但是如果在onResume()或侦听器回调中使用它,它会工作得很好)。

现在避免此问题的最佳方法是使用一个

创建一个扩展的新类。覆盖并返回旧的或新的

然后你可以用它来展示

下面是一个示例,其中包含:

在你所称的活动中:

new MyDialogFragment().show(getSupportFragmentManager(), "tag"); // or getFragmentManager() in API 11+
此答案有助于解释其他三个问题(及其答案):


您可以将对话框的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());
}

这个问题很久以前就得到了回答

然而,这是我自己使用的非黑客的简单的解决方案

我是为自己做的,所以你也可以在你的应用程序中使用它

用途是:

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


只需在活动中添加安卓:configChanges=“orientation” 元素,该元素位于AndroidManifest.xml中

例如:

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

只需使用

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

应用程序将知道如何处理旋转和屏幕大小。

当然,最好的方法是使用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);
    }
}

你可以在我的博客上阅读完整的文章,然后玩。

我遇到了一个类似的问题:当屏幕方向改变时,对话框的
onDismiss
侦听器被调用,即使用户没有关闭对话框。我可以通过使用
onCancel
侦听器来解决这个问题,该侦听器在用户按下后退按钮和用户触摸对话框外部时都会触发。

这似乎仍然是一个问题,即使是在“一切正常”和使用
DialogFragment
等时

有一个线程在其上声明这是由于消息队列中保留了一个旧的disclose消息。提供的解决方法非常简单:

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

        super.onDestroyView();
    }

令人难以置信的是,在该问题首次报告7年后,仍然需要此功能。

如果没有任何帮助,并且您需要一个有效的解决方案,您可以从安全的角度出发,每次打开对话框时,将其基本信息保存到activity ViewModel(并在关闭对话框时将其从该列表中删除)。此基本信息可以是对话框类型和一些id(打开此对话框所需的信息)。此ViewModel在活动生命周期更改期间不会被销毁。假设用户打开一个对话框,留下对餐厅的引用。因此,对话框类型将是LeaveReferenceDialog,id将是餐厅id。打开此对话框时,将此信息保存在一个可以调用DialogInfo的对象中,并将此对象添加到活动的ViewModel中。此信息将允许您在恢复活动时重新打开对话框(
<activity
            android:name=".YourActivity"
            android:configChanges="orientation"
            android:label="@string/app_name"></activity>
ConfigurationChanges = Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize
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);
    }
}
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);
        }
    }
}
    @Override
    public void onDestroyView() {
        /* Bugfix: https://issuetracker.google.com/issues/36929400 */
        if (getDialog() != null && getRetainInstance())
            getDialog().setDismissMessage(null);

        super.onDestroyView();
    }
// On resume in Activity
    override fun onResume() {
            super.onResume()
    
            // Restore dialogs that were open before activity went to background
            restoreDialogs()
        }
    fun restoreDialogs() {
    mainActivityViewModel.setIsRestoringDialogs(true) // lock list in view model

    for (dialogInfo in mainActivityViewModel.openDialogs)
        openDialog(dialogInfo)

    mainActivityViewModel.setIsRestoringDialogs(false) // open lock
}
// Create new dialog
        override fun openLeaveReferenceDialog(restaurantId: String) {
            var dialog = LeaveReferenceDialog()
            // Add id to dialog in bundle
            val bundle = Bundle()
            bundle.putString(Constants.RESTAURANT_ID, restaurantId)
            dialog.arguments = bundle
            dialog.show(supportFragmentManager, "")
        
            // Add dialog info to list of open dialogs
            addOpenDialogInfo(DialogInfo(LEAVE_REFERENCE_DIALOG, restaurantId))
    }
// Dismiss dialog
override fun dismissLeaveReferenceDialog(Dialog dialog, id: String) {
   if (dialog?.isAdded()){
      dialog.dismiss()
      mainActivityViewModel.removeOpenDialog(LEAVE_REFERENCE_DIALOG, id)
   }
}
fun addOpenDialogInfo(dialogInfo: DialogInfo){
    if (!isRestoringDialogs){
       val dialogWasInList = removeOpenDialog(dialogInfo.type, dialogInfo.id)
       openDialogs.add(dialogInfo)
     }
}


fun removeOpenDialog(type: Int, id: String) {
    if (!isRestoringDialogs)
       for (dialogInfo in openDialogs) 
         if (dialogInfo.type == type && dialogInfo.id == id) 
            openDialogs.remove(dialogInfo)
}
    public class AlertRetryDialog extends DialogFragment {

       public interface Listener{
         void onRetry();
         }

    Listener listener;

     public void setListener(Listener listener)
       {
       this.listener=listener;
       }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
    AlertDialog.Builder builder=new AlertDialog.Builder(getActivity());
    builder.setMessage("Please Check Your Network Connection").setPositiveButton("Retry", new 
    DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
             //Screen rotation will cause the listener to be null
            //Always do a null check of your interface listener before calling its method
            if(listener!=null&&listener instanceof HomeFragment)
            listener.onRetry();
        }
       }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
         }
     });
     return builder.create();
    }

   }
                   AlertRetryDialog alertRetryDialog = new AlertRetryDialog();
                    alertRetryDialog.setListener(HomeFragment.this);
                    alertRetryDialog.show(getFragmentManager(), "tag");
              public class YourActivity or YourFragment implements AlertRetryDialog.Listener{ 
                
                  //here's my listener interface's method
                    @Override
                    public void onRetry()
                    {
                     //your code for action
                      }
                
                 }