DialogFragment - 在屏幕旋转后保留监听器

31

我有一个DialogFragment,它创建了一个DatePickerDialog。我使用一个名为newInstance的静态方法来设置初始值,以便使用默认的空构造函数。但是,我应该如何设置监听器呢?在屏幕旋转后,当点击“完成”按钮时,由于监听器不存在,因此监听器不起作用。

public class DatePickerFragment extends DialogFragment {
    public static final String ARG_YEAR = "year";
    public static final String ARG_MONTH = "month";
    public static final String ARG_DAY = "day";

    private OnDateSetListener listener_;

    public static DatePickerFragment newInstance(OnDateSetListener listener, int year, int month, int day) {
        final DatePickerFragment date_picker = new DatePickerFragment();
        date_picker.setListener(listener);

        final Bundle arguments = new Bundle();
        arguments.putInt(ARG_YEAR, year);
        arguments.putInt(ARG_MONTH, month);
        arguments.putInt(ARG_DAY, day);
        date_picker.setArguments(arguments);

        return date_picker;
    }

    private void setListener(OnDateSetListener listener) {
        listener_ = listener;
    }

    @Override
    public Dialog onCreateDialog(Bundle saved_instance_state) {
        final Bundle arguments = getArguments();
        final int year = arguments.getInt(ARG_YEAR);
        final int month = arguments.getInt(ARG_MONTH);
        final int day = arguments.getInt(ARG_DAY);

        return new DatePickerDialog(getActivity(), listener_, year, month, day);
    }
}
3个回答

67

但是,我应该如何设置监听器呢?

您可以在ActivityonCreate方法中更新监听器引用:

private OnDateSetListener mOds = new OnDateSetListener() {

    @Override
    public void onDateSet(DatePicker view, int year, int monthOfYear,
            int dayOfMonth) {
             // do important stuff
    }
};

并且在onCreate方法中:

if (savedInstanceState != null) {
    DatePickerFragment dpf = (DatePickerFragment) getSupportFragmentManager()
            .findFragmentByTag("theTag?");
    if (dpf != null) {
        dpf.setListener(mOds);
    }
}

1
+1,另外需要澄清的是,重要的是使用对话框片段的标签来重新应用侦听器。该标签在对话框片段的“show”方法的第二个参数中指定。 - rekaszeru
1
DialogFragment不是支持库的片段,因此应该使用以下代码:DatePickerFragment dpf = (DatePickerFragment) getFragmentManager() .findFragmentByTag("theTag?");而不是DatePickerFragment dpf = (DatePickerFragment) getSupportFragmentManager() .findFragmentByTag("theTag?"); - Velmurugan V
@VelmuruganV DialogFragment 可在支持库中找到 https://developer.android.com/reference/android/support/v4/app/DialogFragment.html - user
2
抱歉,我不知道。谢谢 @Luksprog 纠正我。 - Velmurugan V
很糟糕,我们不得不处理这些事情。我们应该一起向谷歌报告问题,让他们开发一个好的API,不再强制我们绕过所有东西。 - milosmns
有人对此有任何更新吗?多年来我们一直存在这个问题。 - jettimadhuChowdary

9

在我看来,有一种更高效的方法可以实现这一点,就是使用Fragment生命周期。您可以使用Fragment生命周期回调onAttach()onDetach()自动将Activity强制转换为Listener,代码如下所示:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        _listener = (OnDateSetListener)activity;
    } catch(ClassCastException e) {
        // check if _listener is null before using,
        // or throw new ClassCastException("hosting activity must implement OnDateSetListener");
    }
}

@Override
public void onDetach() {
    super.onDetach();
    _listener = null;
}

这项技术在官方文档中有详细介绍,点击这里查看。


1
是的,但这样做就不能在一个Activity中使用相同的类(比如DialogFragment)并具有不同的监听器了... - ARLabs
1
为什么不呢?只需要在Activity上实现任何listener接口,并将其转换为DialogFragment中的不同listener实例即可。如果您想将某些内容变成可选项,只需要有更复杂的ClassCast错误处理即可。 - d370urn3ur
1
我的意思是,如果同一Activity需要两次使用相同的DialogFragment,并且(如您建议的),Activity继承自侦听器,则在调用活动中的侦听器方法时,您不知道哪种情况...我认为最好使用两个不同的侦听器实例(它们不能是活动)。我所说的基于这样的假设,即为了良好的设计,我们避免循环依赖关系,并且DialogFragment定义了自己的侦听器类型,并且不知道Activity的特殊化。 - ARLabs
我不明白你的意思:“当调用监听器方法时,你不知道是哪种情况”。你的意思是,哪个DialogFragment实例调用了监听器方法?为什么不能在监听器方法中传递有关DialogFragment的标识信息?我需要看一些代码才能理解你的用例。事实是,DialogFragment已经与Activity密切相关,这是处理DialogFragments最常见用法的最简单方法。它也已经被正式记录,因此在生产应用程序中遇到它并不奇怪。 - d370urn3ur
@d370urn3ur,可能ARLabs的意思是Activity可以填充许多扩展相同DialogFragment的片段。在这种情况下,监听器可能会有所不同。 - CoolMind
@ARLabs,我同意你的看法。在这种情况下,我们应该为DialogFragment的每个实例添加一个IntentsetArguments()。在那里,我们将传递该实例的ID(一个随机数,在旋转后不会改变)。我们应该将此ID作为参数传递给侦听器。在Activity中,我们将接收此ID并检查是哪个DialogFragment调用了侦听器。 - CoolMind

2
Luksprog的答案是正确的,我只想指出,解决方案的关键在于findFragmentByTag()函数。因为屏幕旋转后活动也将重新创建,所以您不能调用其成员Fragment变量的setter函数,而应使用此函数找到旧的片段实例。
顺便说一句,当您调用DialogFragment.show()时,标签是第二个参数。

我发现如果你从Fragment中调用DialogFragment,并在屏幕旋转后在onCreateView()中调用fragmentManager.findFragmentByTag(),它可能会返回null。在这种情况下,请尝试调用childFragmentManager.findFragmentByTag()。或者查看**@d370urn3ur**的答案。在这种情况下,您应该在Activity中设置一个回调。 - CoolMind

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