Reputation: 471
I have an AlertDialog
that works fine with Activites
, but it doesn't work with Fragments due to AlertDialog.Builder(getActivity)
. In this case, a parent Activity holds a child Fragment
that uses a RecyclerView
to display records. When the user swipes a record, an AlertDialog
is displayed to confirm the deletion. However, this doesn't work as the AlertDialog
references the parent Activity. I have included the AlertDialog's code and the LogCat output from the AlertFragment's code below.
I instantiate the ConfirmDialogFragment in the child Fragment as follows:
DialogFragment confirmDialog = new ConfirmDialogFragment();
confirmDialog.show(getFragmentManager(), "ConfirmDialogFragment");
The AlertDialog Fragment code is shown below:
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");
}
}
}
The LogCat output shown in the code is shown below.
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}
When ConfirmDialogFragment's Delete is clicked, LogCat displays the following:
ConfirmDialogFragment: Delete clicked
ConfirmDialogFragment: getContext is: com.example.jbiss.petminder.activities.PetInformationActivity@8963dda
ConfirmDialogFragment: this is: com.example.jbiss.petminder.dialogs.ConfirmDialogFragment$2@c3512d0
PetInformationActivity: Entered onDialogPositiveClick
PetInformationViewModel: Entered: deletePetName
As can be see in the LogCat output, ConfirmDialogFragment does return to the calling Fragment's ConfirmDialogFragment.ConfirmDialogListener Interface methods, it goes to the parent Activity's.
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. As can be seen in the LogCat output, trying to use getContext also references the parent Activity. All questions about using AlertDialog
state that getActivity is to be used as they seem to be about using the DialogFragment
, not about a Fragment calling it.
Is AlertDialog intended to be used from a Fragment? Is there a Fragment's equivalent to an Activity's reference that can be passed to the AlertDialog's constructor as shown below?
DialogFragment confirmDialog = new ConfirmDialogFragment(CallingFragmentReferenceSimilarToAnActivity);
confirmDialog.show(getFragmentManager(), "ConfirmDialogFragment");
I should be able to overload the AlertDialog's constructor in that case, I believe.
Upvotes: 0
Views: 1080
Reputation: 1146
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.
The issue is not with the AlertDialog
but with how your listener is set. Since Fragment
's getContext()
method returns context of the parent Activity
.
mListener = (ConfirmDialogListener) context;
This line sets your listener to be the parent Activity
of the calling Fragment
. Instead of setting mListener
to the parent activity, you have to set it to the calling fragment.
First, you have to set the target fragment for your DialogFragment
while instantiating.
DialogFragment confirmDialog = new ConfirmDialogFragment();
confirmDialog.setTargetFragment(this, "CONFIRM_DIALOG_TAG");
if (getFragmentManager() != null) {
confirmDialog.show(getFragmentManager(), "ConfirmDialogFragment");
}
Now, inside your dialog fragment's onCreate
, you can set the callback to be your fragment.
try {
mListener = (ConfirmDialogListener) getTargetFragment();
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement ConfirmDialogListener");
}
And remove the code from onAttach
method.
Upvotes: 1