从 Fragment 调用时可以使 AlertDialog 工作吗?

Can AlertDialog be made to work when called from a Fragment?

我有一个 AlertDialog 可以很好地与 Activites 一起使用,但由于 AlertDialog.Builder(getActivity) 而不能与 Fragments 一起使用。在这种情况下,父 Activity 持有子 Fragment,子 Fragment 使用 RecyclerView 显示记录。当用户滑动记录时,会显示一个 AlertDialog 以确认删除。但是,这不起作用,因为 AlertDialog 引用父 Activity。我在下面包含了 AlertDialog 的代码和 AlertFragment 代码的 LogCat 输出。

我在子片段中实例化ConfirmDialogFragment如下:

DialogFragment confirmDialog =  new ConfirmDialogFragment();
confirmDialog.show(getFragmentManager(), "ConfirmDialogFragment");

AlertDialog Fragment代码如下所示:

package com.example.jbiss.petminder.dialogs;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import androidx.fragment.app.DialogFragment;
import com.example.jbiss.petminder.R;
import java.util.Set;

public class ConfirmDialogFragment extends DialogFragment {
    private static final String TAG = "ConfirmDialogFragment";
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Log.d(TAG, "Entered onCreateDialog");
        if(savedInstanceState ==null){
            Log.d(TAG, "savedInstanceState is null");
        }else{
            Set<String> s = savedInstanceState.keySet();
            Log.d(TAG, "savedInstanceState is: " + s);
        }
        Log.d(TAG, "getParentFragment is: " + getParentFragment());
        Log.d(TAG, "getContext is: " + getContext());
        Log.d(TAG, "getActivity is: " + getActivity());
        Log.d(TAG, "this is: " + this);
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(R.string.delete_confirm)
                .setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        Log.d(TAG, "Delete clicked");
                        Log.d(TAG, "getContext is: " + getContext());
                        Log.d(TAG, "this is: " + this);
                        mListener.onDialogPositiveClick(ConfirmDialogFragment.this);
                    }
                })
                .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        // User cancelled the dialog
                        Log.d(TAG, "Cancel clicked");
                        mListener.onDialogNegativeClick(ConfirmDialogFragment.this);
                    }
                }); 
        return builder.create();
    }

    public interface ConfirmDialogListener {
        public void onDialogPositiveClick(DialogFragment dialog);
        public void onDialogNegativeClick(DialogFragment dialog);
    } 

    // Use this instance of the interface to deliver action events
    ConfirmDialogListener mListener;

    // Override the Fragment.onAttach() method to instantiate the ConfirmDialogListener
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.d(TAG, "Entered onAttach");
        // Verify that the host activity implements the callback interface
        try {
            // Instantiate the NoticeDialogListener so we can send events to the host
            mListener = (ConfirmDialogListener) context;
            Log.d(TAG, "activity is: " + context);
            Log.d(TAG, "mListener is: " + mListener);
        } catch (ClassCastException e) {
            // The activity doesn't implement the interface, throw exception
            throw new ClassCastException(context.toString()
                    + " must implement NoticeDialogListener");
        }
    }
}

代码中的LogCat输出如下所示。

MedicalInformationFragment: swiped to the 4!
MedicalInformationFragment: position swiped is: 0
ConfirmDialogFragment: Entered onAttach
ConfirmDialogFragment: activity is: com.example.jbiss.petminder.activities.PetInformationActivity@8963dda
ConfirmDialogFragment: mListener is: com.example.jbiss.petminder.activities.PetInformationActivity@8963dda
ConfirmDialogFragment: Entered onCreateDialog
ConfirmDialogFragment: savedInstanceState is null
ConfirmDialogFragment: getParentFragment is: null
ConfirmDialogFragment: getContext is: com.example.jbiss.petminder.activities.PetInformationActivity@8963dda
ConfirmDialogFragment: getActivity is: com.example.jbiss.petminder.activities.PetInformationActivity@8963dda
ConfirmDialogFragment: this is: ConfirmDialogFragment{a4e01a0 #2 ConfirmDialogFragment}

点击ConfirmDialogFragment的删除时,LogCat显示如下:

ConfirmDialogFragment: Delete clicked
ConfirmDialogFragment: getContext is: com.example.jbiss.petminder.activities.PetInformationActivity@8963dda
ConfirmDialogFragment: this is: com.example.jbiss.petminder.dialogs.ConfirmDialogFragment@c3512d0
PetInformationActivity: Entered onDialogPositiveClick
PetInformationViewModel: Entered: deletePetName

正如在 LogCat 输出中所见,ConfirmDialogFragment 对调用 Fragment 的 ConfirmDialogFragment.ConfirmDialogListener 接口方法执行 return,它转到父 Activity。

这是因为 AlertDialog.Builder 是用 getActivity() 实例化的,它获取父 Activity 的引用,而不是调用片段的引用。正如在 LogCat 输出中所见,尝试使用 getContext 也引用了父 Activity。所有关于使用 AlertDialog 的问题都表明要使用 getActivity,因为它们似乎是关于使用 DialogFragment,而不是关于调用它的 Fragment。

AlertDialog 是否旨在从 Fragment 中使用?是否有片段等同于 Activity 的引用,可以传递给 AlertDialog 的构造函数,如下所示?

DialogFragment confirmDialog =  new ConfirmDialogFragment(CallingFragmentReferenceSimilarToAnActivity);
confirmDialog.show(getFragmentManager(), "ConfirmDialogFragment");

我相信在那种情况下我应该能够重载 AlertDialog 的构造函数。

This is due to the fact that AlertDialog.Builder is instantiated with getActivity() that gets the parent Activity's reference, NOT the calling Fragment's.

问题不在于 AlertDialog,而在于你的监听器是如何设置的。由于 FragmentgetContext() 方法 returns 父上下文 Activity.

mListener = (ConfirmDialogListener) context;

此行将您的侦听器设置为调用 Fragment 的父级 Activity。您必须将其设置为调用片段,而不是将 mListener 设置为父级 activity。

首先,您必须在实例化时为 DialogFragment 设置目标片段。

DialogFragment confirmDialog =  new ConfirmDialogFragment();
confirmDialog.setTargetFragment(this, "CONFIRM_DIALOG_TAG");
if (getFragmentManager() != null) {
  confirmDialog.show(getFragmentManager(), "ConfirmDialogFragment");
}

现在,在您的对话片段 onCreate 中,您可以将回调设置为您的片段。

try {
  mListener = (ConfirmDialogListener) getTargetFragment();
} catch (ClassCastException e) {
  throw new ClassCastException(context.toString()
                + " must implement ConfirmDialogListener");
}

并从 onAttach 方法中删除代码。