【问题标题】:How to retain listener on custom dialog opened from fragment?如何在从片段打开的自定义对话框上保留侦听器?
【发布时间】:2023-04-01 11:51:01
【问题描述】:

我遇到了一些障碍。我有一个非常类似于以下描述的场景:DialogFragment - retaining listener after screen rotation

建议的解决方案对作者来说很好,因为他的对话框是从活动中调用的。我的情况完全相同,但我的自定义对话框是从片段而不是活动中调用的。 (IE Activity->Fragment->Dialog)

我实现了相同的解决方案(在调用片段的 onResume 中设置侦听器),但在这种情况下不起作用。

似乎正在发生的是,当屏幕旋转时,Android 会杀死 Dialog 和 Fragment。然后按该顺序重新创建它们。因此,当在我的自定义对话框上调用我的 onCreateDialog 时,包含的 Fragment 尚未重新创建,因此侦听器为正负按钮设置为 null。

有人知道解决这个问题的方法吗?

public class RecipeDetailEditFragment extends SherlockFragment implements DialogInterface.OnClickListener {
    private EditStepFragmentDialog stepDialog;
    private Recipe newRecipe; //main data object implements parcelable
    ...
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        stepDialog = EditStepFragmentDialog.newInstance(newRecipe);
        //I've also tried passing 'this' into the newInstance constructor and 
        //setting the listener there, but that doesn't work either
    }

    public void onResume() {
        stepDialog.setListener(this);
        super.onResume();
    }
    ...
}


public class EditStepFragmentDialog extends DialogFragment {
    private DialogInterface.OnClickListener ocl;
    private static final String ARG_RECIPE = "recipe";
    private Recipe recipe;

    public EditStepFragmentDialog() {}

    public static EditStepFragmentDialog newInstance(Recipe rec) { //(Recipe rec, DialogInterface.OnClickListener oc) as mentioned doesn't work.
        EditStepFragmentDialog dia = new EditStepFragmentDialog();
        Bundle args = new Bundle();
        args.putParcelable(ARG_RECIPE, rec);

        //dia.setListener(oc);
        return dia;
    }

    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder adb = new AlertDialog.Builder(getActivity());

        if (getArguments().containsKey(ARG_RECIPE)) {
            recipe = (Recipe) getArguments().getParcelable(ARG_RECIPE);
        }
        ...

        adb.setPositiveButton("Done", ocl);
        adb.setNegativeButton("Cancel", ocl);

        ...

        return adb.create();
    }

    public void setListener(DialogInterface.OnClickListener cl) {
        ocl = cl;
    }
}

【问题讨论】:

  • 你必须发布代码

标签: android android-fragments android-lifecycle android-dialogfragment


【解决方案1】:

我浏览了所讨论链接上的所有选项,但没有一个解决方案最终对我有用。在进一步搜索后,我还尝试了一些其他选项,例如 get/setTargetFragment 和 FragmentManager.put/getFragment。这些对我也不起作用。然后我又看了一遍:

http://developer.android.com/training/basics/fragments/communicating.html

他们特别说“两个片段不应该直接通信”。我认为这是真正被证明是正确的案例之一。

我最终实现了那里提供的建议回调机制,并得到了这样的结果:

在 DialogFragment 中:

public interface OnEditStepDialogListener {
    public void onEditStepDialogPositive(int pos, String description);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    try {
        mCallback = (OnEditStepDialogListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement OnEditStepDialogListener");
    }
}

在托管活动中:

public class MyActivity extends SherlockFragmentActivity implements EditStepFragmentDialog.OnEditStepDialogListener {

...

@Override
public void onEditStepDialogPositive(int pos, String desc) {
    FragmentManager fm = getSupportFragmentManager();
    RecipeDetailEditFragment ef = (RecipeDetailEditFragment)fm.findFragmentByTag(RecipeDetailEditFragment.TAG);

    ef.applyStepEdit(pos, desc);
}

在触发 FragmentDialog 的 Fragment 中:

public static final String TAG = "tag1";

public void applyStepEdit(int pos, String description) {
    ...
}

这很完美,如果打开然后方向更改和编辑完成,它实际上会触发我需要在调用片段中运行的最终功能,而不是崩溃或不做任何事情(空侦听器)。

【讨论】:

  • 太好了,您找到了解决方案!只是一个小注释,这使您能够扩展片段以在创建时采用另一个参数,一个 OnEditStepDialogListener 来处理回调,以防万一您不喜欢处理 Activity 类中的每个回调。
【解决方案2】:

报告您的活动并创建对话会是一场灾难吗?

我刚刚查看了我的代码,看看我在做什么,因为我没有遇到这个问题。我的看法是这样的:

- MyActivity
     |
     ---- MapsFragmet (for example)
     |
     ---- DirectionsModule (simple class that is handed Context)
     |
     ---- PointsOfInterestModule (simple class that is handed Context)

因此,在这种构造中,Activity 仅使用片段来显示地图,但可以将其用于方向或兴趣点的目的,具体取决于调用的模块。

现在,当模块遇到问题或需要用户交互时,它会向 MyActivity 报告,然后它会显示一个 DialogFragment。

想给出一个更好的答案,因为我不明白为什么你不能从另一个 Fragment 中调用 DialogFragment 并期待一个好的行为。

以防万一,您是否在 Fragment 上设置了 setRetainInstance(true)?

编辑:

好的,所以我刚刚查看了您新提交的代码,这是我的新想法:

扩展对话框的参数以获取上下文,以便您可以这样调用它:

stepDialog = EditStepFragmentDialog.newInstance(getActivity(), newRecipe);

接下来在对话框中使用添加的上下文而不是 getActivity():

AlertDialog.Builder adb = new AlertDialog.Builder(context);

我怀疑(不确定)SherlockFragment 算作 Activity 的实例,因此当您在对话框中调用 getActivity() 时,它会与您的片段绑定。

【讨论】:

  • 这不会是一场灾难,但我更喜欢允许片段响应它触发的对话的解决方案。如果没有其他人有更好的选择,我会认为这是最好的答案。我已经在片段上尝试了 setRetainInstance,但它似乎没有帮助。
  • 刚刚做了一个新的编辑(你会自动得到通知吗?)
  • 我只是尝试这样做(内部上下文,并在 getInstance 构造函数中调用 setContext 函数来设置它),现在如果在显示弹出窗口后旋转它会崩溃。我很确定这与 Listener 问题的原因相同......对话框在 Fragment 之前被重新创建,所以当它第一次运行 onCreateDialog 时,内部上下文对象尚未设置......
  • 好的,现在我想我找到了解决方案:stackoverflow.com/questions/8235080/… 您需要向下滚动一下才能找到答案。
  • 您所指的答案的发布者是谁?在问这个问题之前,我查看了该帖子,据我所知,大多数答案都集中在如何让您的保留实例包正常工作。侦听器不可打包,所以这不考虑到我的场景。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-30
  • 1970-01-01
  • 2014-07-29
  • 1970-01-01
  • 2014-04-08
相关资源
最近更新 更多