Android DialogFragment与Dialog的比较

274

谷歌建议我们使用DialogFragment而非简单的Dialog,使用Fragments API,但在处理简单的是/否确认信息时,使用孤立的DialogFragment是荒谬的。在这种情况下,最佳实践是什么?


10
简而言之,简单地使用 DialogAlertDialog.Builder::create()::show() 创建的对话框将在旋转屏幕时消失。 - WSBT
10个回答

92

是的,使用DialogFragment,在onCreateDialog中,您可以简单地使用AlertDialog构建器来创建一个带有“是/否”确认按钮的简单AlertDialog。需要的代码不多。

关于在您的片段中处理事件,有各种方法可供选择,但我通常在我的Fragment中定义一个消息Handler,通过它的构造函数将其传递给DialogFragment,然后在各种点击事件上将消息传递回我的片段处理程序。这样做也有各种方法,但以下方法适用于我。

在对话框中,保存消息并在构造函数中实例化:

private Message okMessage;
...
okMessage = handler.obtainMessage(MY_MSG_WHAT, MY_MSG_OK);

在您的对话框中实现 onClickListener,然后根据需要调用处理程序:

public void onClick(.....
    if (which == DialogInterface.BUTTON_POSITIVE) {
        final Message toSend = Message.obtain(okMessage);
        toSend.sendToTarget();
    }
 }

编辑

由于Message是可包裹的,所以您可以在onSaveInstanceState中保存它,并恢复它。

outState.putParcelable("okMessage", okMessage);

然后在onCreate

if (savedInstanceState != null) {
    okMessage = savedInstanceState.getParcelable("okMessage");
}

4
问题不在于 okMessage 本身,而是当你从 Bundle 中加载它时,okMessage 的 target 属性会变成 null。如果一个 Message 的 target 是 null,并且你使用了 sendToTarget 方法,你会得到一个 NullPointerException 异常,这不是因为 Message 本身为 null,而是因为它的 target 为 null。 - hrnt
3
使用DialogFragment相比于使用Dialog的优势是什么? - Raphael Petegrosso
101
使用DialogFragment的优势在于其自动处理对话框的完整生命周期,因此你再也不会遇到“对话框泄漏”的错误了。尽管放心使用DialogFragment,忘记使用Dialogs吧。 - Snicolas
7
我认为应该使用 setArguments() 和 getArguments() 来传递 okMessage,而不是通过构造函数传递。 - pjv
1
我很容易使用Builder,并且通过android:configChanges="locale|keyboardHidden|orientation|screenSize"来处理活动管理,我在应用程序中没有看到任何问题... - Renetik
显示剩余9条评论

73

如果你在应用程序中经常使用对话框,可以创建一般的DialogFragment子类,如YesNoDialog和OkDialog,并传入标题和消息。

public class YesNoDialog extends DialogFragment
{
    public static final String ARG_TITLE = "YesNoDialog.Title";
    public static final String ARG_MESSAGE = "YesNoDialog.Message";

    public YesNoDialog()
    {

    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        Bundle args = getArguments();
        String title = args.getString(ARG_TITLE);
        String message = args.getString(ARG_MESSAGE);

        return new AlertDialog.Builder(getActivity())
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null);
                }
            })
            .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, null);
                }
            })
            .create();
    }
}

然后使用以下方式调用:

    DialogFragment dialog = new YesNoDialog();
    Bundle args = new Bundle();
    args.putString(YesNoDialog.ARG_TITLE, title);
    args.putString(YesNoDialog.ARG_MESSAGE, message);
    dialog.setArguments(args);
    dialog.setTargetFragment(this, YES_NO_CALL);
    dialog.show(getFragmentManager(), "tag");

onActivityResult中处理结果。


是的,DialogFragment 会为您处理所有生命周期事件。 - ashishduh
1
我认为它不起作用,因为旋转后旧对话框仍然存在,并且它保持分配给旧的不存在的片段(dialog.setTargetFragment(this, YES_NO_CALL);),因此在旋转后getTargetFragment().onActivityResult无法正常工作。 - Malachiasz
7
YES_NO_CALL是什么,getFragmentManager()onActivityResult又分别是什么?
  • YES_NO_CALL:无法确定具体指的是哪个概念或者上下文,请提供更多信息。
  • getFragmentManager():这是一个Android应用编程接口(API),用于获取当前Activity的FragmentManager对象。FragmentManager用于管理Fragment,可以在Activity中添加、删除和替换Fragment。
  • onActivityResult:这是一个回调方法,当启动的Activity完成后,通过该方法向发起Activity传递数据。通常情况下,在发起Activity中使用startActivityForResult()方法启动其他Activity,当被启动的Activity完成后会返回结果,并在发起Activity中触发onActivityResult()方法来处理结果。
- msysmilu
2
YES_NO_CALL 是一个自定义整数,用作请求代码。getFragmentManager() 获取活动的片段管理器,而 onActivityResult() 则是片段生命周期回调方法。 - ashishduh
3
请将 getFragmentManager() 替换为 getSupportFragmentManager()。 - Avinash Verma
显示剩余7条评论

34

使用DialogFragment取代AlertDialog:


  • 自API级别13引入以来:

    Activity的showDialog方法已被弃用。 在其他代码中调用对话框不建议,因为您将不得不自己管理对话框(例如屏幕方向更改)。

  • DialogFragment和AlertDialog的区别

    它们有很大的区别吗?从Android参考文献中关于DialogFragment的描述:

    DialogFragment是一个显示在其活动窗口上方的对话框窗口片段。此片段包含一个Dialog对象,根据片段的状态适当地显示该对象。应通过此处提供的API 而不是直接调用对话框来控制对话框(决定何时显示、隐藏、取消对话框)。

  • 其他注意事项

    • 由于设备具有不同的屏幕尺寸,Fragment是Android框架的自然演变。
    • DialogFragments和Fragments可在支持库中使用,使该类在所有当前使用的Android版本中可用。

不足之处在于你必须自己创建一个类。这是一大堆毫无意义的样板代码。我认为谷歌在这里搞砸了。你可以在一行代码中实例化一个AlertDialog,但无法在DialogFragment中实现。更不用说FragmentManager的混乱了。 - undefined

29

我建议使用DialogFragment

当然,使用它创建一个“是/否”对话框相当复杂,考虑到这应该是一个相当简单的任务,但是使用Dialog创建类似的对话框也出奇地复杂。

(活动生命周期使得它变得复杂 - 必须让Activity管理对话框的生命周期 - 如果使用8以下的API级别,则无法通过Activity.showDialog传递自定义参数,例如自定义消息)

好处是,通常可以很容易地在DialogFragment之上构建自己的抽象。


你将如何处理警告对话框回调(是,否)? - Alexey Zakharov
最简单的方法是在托管活动中实现一个带有String参数的方法。例如,当用户点击“Yes”时,对话框将调用活动的方法并传递“agree”参数。这些参数是在显示对话框时指定的,例如AskDialog.ask("Do you agree to these terms?", "agree", "disagree");。 - hrnt
5
但我需要在片段内使用回调函数,而不是活动中。我可以使用 setTargetFragment 并将其转换为接口,但这很麻烦。 - Alexey Zakharov
你也可以通过将标签设置为目标,并使用FragmentManagerfindFragmentByTag来获取目标片段。但是,这需要相当多的代码。 - hrnt
@AlexeyZakharov 我知道这已经晚了5年,但你可以将Fragmentthis传递,并让你的Activity实现你的Interface。但要小心线程问题,如果并发没有得到控制,你可能会在不需要它们时弹出接口调用。不确定这对内存和循环依赖关系有什么影响,还有其他人想加入讨论吗?另一个选择是使用Message/ Handler,但你仍然可能会遇到并发问题。 - tricknology

9

DialogFragment基本上是一个可以用作对话框的Fragment。

出于以下原因,使用DialogFragment而不是Dialog:

  • DialogFragment在配置更改和保存恢复流程后会自动重新创建
  • DialogFragment继承了完整的Fragment生命周期
  • 不再出现IllegalStateExceptions和泄露窗口崩溃。当活动被销毁时,警告对话框仍然存在这种情况很常见。

更多细节


8

我建议对 @ashishduh 的回答进行简化:

public class AlertDialogFragment extends DialogFragment {
public static final String ARG_TITLE = "AlertDialog.Title";
public static final String ARG_MESSAGE = "AlertDialog.Message";

public static void showAlert(String title, String message, Fragment targetFragment) {
    DialogFragment dialog = new AlertDialogFragment();
    Bundle args = new Bundle();
    args.putString(ARG_TITLE, title);
    args.putString(ARG_MESSAGE, message);
    dialog.setArguments(args);
    dialog.setTargetFragment(targetFragment, 0);
    dialog.show(targetFragment.getFragmentManager(), "tag");
}

public AlertDialogFragment() {}

@NonNull
@Override
public AlertDialog onCreateDialog(Bundle savedInstanceState)
{
    Bundle args = getArguments();
    String title = args.getString(ARG_TITLE, "");
    String message = args.getString(ARG_MESSAGE, "");

    return new AlertDialog.Builder(getActivity())
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null);
                }
            })
            .create();
}

它消除了用户(类的使用者)对组件内部结构的熟悉需求,使得使用变得非常简单:
AlertDialogFragment.showAlert(title, message, this);

P.S. 在我的情况下,我需要一个简单的警示对话框,所以我创建了这个。您可以将此方法应用于您需要的“是/否”或任何其他类型。


8

使用构建器模式创建通用的AlertDialogFragment

在我的项目中,我已经在很多地方使用了AlertDialog.Builder,但后来发现它存在问题。然而,我不想在应用程序的任何地方都修改那么多代码。此外,我实际上是一个喜欢将OnClickListeners作为匿名类传递到需要它们的地方(也就是使用setPositiveButton()setNegativeButton()等时)的粉丝,而不是必须实现成千上万个回调方法来在对话框片段和持有者片段之间进行通信,这可能会导致非常混乱和复杂的代码。特别是,如果您在一个片段中有多个不同的对话框,然后需要在回调实现中区分当前显示的对话框。

因此,我结合了不同的方法创建了一个通用的AlertDialogFragment帮助类,可以像使用AlertDialog一样使用:


解决方案

请注意,我的代码中使用了Java 8 lambda表达式,因此如果您还没有使用lambda表达式,则可能需要更改代码的某些部分。)

/**
 * Helper class for dialog fragments to show a {@link AlertDialog}. It can be used almost exactly
 * like a {@link AlertDialog.Builder}
 * <p />
 * Creation Date: 22.03.16
 *
 * @author felix, http://flx-apps.com/
 */
public class AlertDialogFragment extends DialogFragment {
    protected FragmentActivity activity;
    protected Bundle args;
    protected String tag = AlertDialogFragment.class.getSimpleName();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activity = getActivity();
        args = getArguments();
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog = setDialogDefaults(new AlertDialog.Builder(getActivity())).create();

        if (args.containsKey("gravity")) {
            dialog.getWindow().getAttributes().gravity = args.getInt("gravity");
        }

        dialog.setOnShowListener(d -> {
            if (dialog != null && dialog.findViewById((android.R.id.message)) != null) {
                ((TextView) dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
            }
        });
        return dialog;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);

        if (args.containsKey("onDismissListener")) {
            Parcelable onDismissListener = args.getParcelable("onDismissListener");
            if (onDismissListener != null && onDismissListener instanceof ParcelableOnDismissListener) {
                ((ParcelableOnDismissListener) onDismissListener).onDismiss(this);
            }
        }
    }

    /**
     * Sets default dialog properties by arguments which were set using {@link #builder(FragmentActivity)}
     */
    protected AlertDialog.Builder setDialogDefaults(AlertDialog.Builder builder) {
        args = getArguments();
        activity = getActivity();

        if (args.containsKey("title")) {
            builder.setTitle(args.getCharSequence("title"));
        }

        if (args.containsKey("message")) {
            CharSequence message = args.getCharSequence("message");
            builder.setMessage(message);
        }

        if (args.containsKey("viewId")) {
            builder.setView(getActivity().getLayoutInflater().inflate(args.getInt("viewId"), null));
        }

        if (args.containsKey("positiveButtonText")) {
            builder.setPositiveButton(args.getCharSequence("positiveButtonText"), (dialog, which) -> {
                onButtonClicked("positiveButtonListener", which);
            });
        }

        if (args.containsKey("negativeButtonText")) {
            builder.setNegativeButton(args.getCharSequence("negativeButtonText"), (dialog, which) -> {
                onButtonClicked("negativeButtonListener", which);
            });
        }

        if (args.containsKey("neutralButtonText")) {
            builder.setNeutralButton(args.getCharSequence("neutralButtonText"), (dialog, which) -> {
                onButtonClicked("neutralButtonListener", which);
            });
        }

        if (args.containsKey("items")) {
            builder.setItems(args.getStringArray("items"), (dialog, which) -> {
                onButtonClicked("itemClickListener", which);
            });
        }

        // @formatter:off
        // FIXME this a pretty hacky workaround: we don't want to show the dialog if onClickListener of one of the dialog's button click listener were lost
        //       the problem is, that there is no (known) solution for parceling a OnClickListener in the long term (only for state changes like orientation change,
        //       but not if the Activity was completely lost)
        if (
                (args.getParcelable("positiveButtonListener") != null && !(args.getParcelable("positiveButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("negativeButtonListener") != null && !(args.getParcelable("negativeButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("neutralButtonListener") != null && !(args.getParcelable("neutralButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("itemClickListener") != null && !(args.getParcelable("itemClickListener") instanceof ParcelableOnClickListener))
        ) {
            new DebugMessage("Forgot onClickListener. Needs to be dismissed.")
                    .logLevel(DebugMessage.LogLevel.VERBOSE)
                    .show();
            try {
                dismissAllowingStateLoss();
            } catch (NullPointerException | IllegalStateException ignored) {}
        }
        // @formatter:on

        return builder;
    }

    public interface OnDismissListener {
        void onDismiss(AlertDialogFragment dialogFragment);
    }

    public interface OnClickListener {
        void onClick(AlertDialogFragment dialogFragment, int which);
    }

    protected void onButtonClicked(String buttonKey, int which) {
        ParcelableOnClickListener parcelableOnClickListener = getArguments().getParcelable(buttonKey);
        if (parcelableOnClickListener != null) {
            parcelableOnClickListener.onClick(this, which);
        }
    }

    // region Convenience Builder Pattern class almost similar to AlertDialog.Builder
    // =============================================================================================

    public AlertDialogFragment builder(FragmentActivity activity) {
        this.activity = activity;
        this.args = new Bundle();
        return this;
    }

    public AlertDialogFragment addArguments(Bundle bundle) {
        args.putAll(bundle);
        return this;
    }

    public AlertDialogFragment setTitle(int titleStringId) {
        return setTitle(activity.getString(titleStringId));
    }

    public AlertDialogFragment setTitle(CharSequence title) {
        args.putCharSequence("title", title);
        return this;
    }

    public AlertDialogFragment setMessage(int messageStringId) {
        return setMessage(activity.getString(messageStringId));
    }

    public AlertDialogFragment setMessage(CharSequence message) {
        args.putCharSequence("message", message);
        return this;
    }

    public AlertDialogFragment setPositiveButton(int textStringId, OnClickListener onClickListener) {
        return setPositiveButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setPositiveButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("positiveButtonText", text);
        args.putParcelable("positiveButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setNegativeButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) {
        return setNegativeButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setNegativeButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("negativeButtonText", text);
        args.putParcelable("negativeButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setNeutralButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) {
        return setNeutralButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setNeutralButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("neutralButtonText", text);
        args.putParcelable("neutralButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setOnDismissListener(OnDismissListener onDismissListener) {
        if (onDismissListener == null) {
            return this;
        }

        Parcelable p = new ParcelableOnDismissListener() {
            @Override
            public void onDismiss(AlertDialogFragment dialogFragment) {
                onDismissListener.onDismiss(dialogFragment);
            }
        };
        args.putParcelable("onDismissListener", p);
        return this;
    }

    public AlertDialogFragment setItems(String[] items, AlertDialogFragment.OnClickListener onClickListener) {
        args.putStringArray("items", items);
        args.putParcelable("itemClickListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setView(int viewId) {
        args.putInt("viewId", viewId);
        return this;
    }

    public AlertDialogFragment setGravity(int gravity) {
        args.putInt("gravity", gravity);
        return this;
    }

    public AlertDialogFragment setTag(String tag) {
        this.tag = tag;
        return this;
    }

    public AlertDialogFragment create() {
        setArguments(args);
        return AlertDialogFragment.this;
    }

    public AlertDialogFragment show() {
        create();
        try {
            super.show(activity.getSupportFragmentManager(), tag);
        }
        catch (IllegalStateException e1) {

            /**
             * this whole part is used in order to attempt to show the dialog if an
             * {@link IllegalStateException} was thrown (it's kinda comparable to
             * {@link FragmentTransaction#commitAllowingStateLoss()} 
             * So you can remove all those dirty hacks if you are sure that you are always
             * properly showing dialogs in the right moments
             */

            new DebugMessage("got IllegalStateException attempting to show dialog. trying to hack around.")
                    .logLevel(DebugMessage.LogLevel.WARN)
                    .exception(e1)
                    .show();

            try {
                Field mShownByMe = DialogFragment.class.getDeclaredField("mShownByMe");
                mShownByMe.setAccessible(true);
                mShownByMe.set(this, true);
                Field mDismissed = DialogFragment.class.getDeclaredField("mDismissed");
                mDismissed.setAccessible(true);
                mDismissed.set(this, false);
            }
            catch (Exception e2) {
                new DebugMessage("error while showing dialog")
                        .exception(e2)
                        .logLevel(DebugMessage.LogLevel.ERROR)
                        .show();
            }
            FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
            transaction.add(this, tag);
            transaction.commitAllowingStateLoss(); // FIXME hacky and unpredictable workaround
        }
        return AlertDialogFragment.this;
    }

    @Override
    public int show(FragmentTransaction transaction, String tag) {
        throw new NoSuchMethodError("Please use AlertDialogFragment.show()!");
    }

    @Override
    public void show(FragmentManager manager, String tag) {
        throw new NoSuchMethodError("Please use AlertDialogFragment.show()!");
    }

    protected ParcelableOnClickListener createParcelableOnClickListener(AlertDialogFragment.OnClickListener onClickListener) {
        if (onClickListener == null) {
            return null;
        }

        return new ParcelableOnClickListener() {
            @Override
            public void onClick(AlertDialogFragment dialogFragment, int which) {
                onClickListener.onClick(dialogFragment, which);
            }
        };
    }

    /**
     * Parcelable OnClickListener (can be remembered on screen rotation)
     */
    public abstract static class ParcelableOnClickListener extends ResultReceiver implements AlertDialogFragment.OnClickListener {
        public static final Creator<ResultReceiver> CREATOR = ResultReceiver.CREATOR;

        ParcelableOnClickListener() {
            super(null);
        }

        @Override
        public abstract void onClick(AlertDialogFragment dialogFragment, int which);
    }

    /**
     * Parcelable OnDismissListener (can be remembered on screen rotation)
     */
    public abstract static class ParcelableOnDismissListener extends ResultReceiver implements AlertDialogFragment.OnDismissListener {
        public static final Creator<ResultReceiver> CREATOR = ResultReceiver.CREATOR;

        ParcelableOnDismissListener() {
            super(null);
        }

        @Override
        public abstract void onDismiss(AlertDialogFragment dialogFragment);
    }


    // =============================================================================================
    // endregion
}

用法

// showing a normal alert dialog with state loss on configuration changes (like device rotation)
new AlertDialog.Builder(getActivity())
        .setTitle("Are you sure? (1)")
        .setMessage("Do you really want to do this?")
        .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(), "Yes clicked", Toast.LENGTH_SHORT).show())
        .setNegativeButton("Cancel", null)
        .show();

// showing a dialog fragment using the helper class with no state loss on configuration changes
new AlertDialogFragment.builder(getActivity())
        .setTitle("Are you sure? (2)")
        .setMessage("Do you really want to do this?")
        .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(), "Yes clicked", Toast.LENGTH_SHORT).show())
        .setNegativeButton("Cancel", null)
        .show();

我不仅在这里发布我的解决方案,还想问你们的意见:这种方法是否合理或存在问题?


5
这是一个非常有趣的想法,但我认为API设计存在问题。如果您将OnClickListener传递给setPositiveButton(),当设备旋转并从Bundle args重新创建片段时,OnClickListeners无法从Parcelable中正确地重新创建。根本问题在于您无法在旋转期间重新创建侦听器,但API接口(接受接口)要求这样做。我希望这不是这种情况(因为我喜欢这个想法)。 - Xargs
3
好的想法,但是就像@Xargs所说的那样,它不起作用。传入的监听器在旋转时没有被正确地重新创建。 - Graham Borland
我的测试结果是,它在旋转和恢复应用程序(例如返回主屏幕后)时确实有效,但在活动被完全销毁后恢复时则无效(此时OnClickListeners确实丢失)。(在Android 4.4.4和Android 5.1.1上进行了测试) - flxapps
我还没有测试过这个具体的实现,但从我测试过的情况来看,传递给片段包的可包含监听器在重新创建时被正确调用。我不知道为什么,但它似乎可以工作。 - Saad Farooq
@flxapps,在自定义视图中,如何获取子视图并更改其属性或应用侦听器?在您的类中,您没有返回任何对话框实例,如果有人尝试获取子视图,则可能会引发异常。 - Zubair Rehman
看看你那漂亮的330行代码的解决方案,告诉我Google不是完全让我们开发者受罪,强迫我们使用如此冗长的代码来显示一个简单的是/否对话框。这简直是疯狂至极。 - undefined

4

使用 Dialog 进行简单的是或否对话框。

当你需要更复杂的视图,并且需要获取生命周期,比如 oncreate、请求权限、任何生命周期覆盖时,建议使用对话框片段(Dialog Fragment)。这样可以将权限和任何其他代码分离,使得对话框能够正常运作而无需与调用活动通信。


3

对话框:对话框是一个小窗口,提示用户进行决策或输入其他信息。

DialogFragment:DialogFragment是一种特殊的片段子类,专门用于创建和托管对话框。它允许FragmentManager管理对话框的状态,并在配置更改发生时自动恢复对话框。


2

DialogFragment具备对话框和片段的功能。基本上,所有的生命周期事件都由DialogFragment自动管理,例如屏幕配置更改等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接