Можно ли заставить AlertDialog работать при вызове из фрагмента? - PullRequest
0 голосов
/ 27 марта 2019

У меня есть AlertDialog, который отлично работает с Activites, но не работает с фрагментами из-за AlertDialog.Builder(getActivity). В этом случае родительский Activity содержит дочерний элемент Fragment, который использует RecyclerView для отображения записей. Когда пользователь проводит запись, отображается AlertDialog, чтобы подтвердить удаление. Однако это не работает, так как AlertDialog ссылается на родительское действие. Я включил код AlertDialog и вывод LogCat из кода AlertFragment ниже.

Я создаю экземпляр ConfirmDialogFragment в дочернем фрагменте следующим образом:

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

Код фрагмента AlertDialog показан ниже:

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$2@c3512d0
PetInformationActivity: Entered onDialogPositiveClick
PetInformationViewModel: Entered: deletePetName

Как можно видеть из выходных данных LogCat, ConfirmDialogFragment возвращается к методам интерфейса ConfirmDialogFragment.ConfirmDialogListener вызывающего фрагмента, он переходит к родительской операции Activity.

Это связано с тем, что AlertDialog.Builder создается с getActivity(), который получает ссылку на родительскую активность, а не на вызывающий фрагмент. Как видно из вывода LogCat, попытка использовать getContext также ссылается на родительскую активность. Все вопросы об использовании AlertDialog утверждают, что getActivity следует использовать так, как они, похоже, касаются использования DialogFragment, а не фрагмента, вызывающего его.

AlertDialog предназначен для использования с фрагмента? Есть ли фрагмент, эквивалентный ссылке на Activity, который можно передать конструктору AlertDialog, как показано ниже?

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

Я должен быть в состоянии перегрузить конструктор AlertDialog в этом случае, я считаю.

1 Ответ

1 голос
/ 27 марта 2019

Это связано с тем, что AlertDialog.Builder создается с помощью getActivity (), который получает ссылку на родительскую активность, а не на вызывающий фрагмент.

Проблема не в AlertDialog, а в том, как настроен ваш слушатель. Так как Fragment метод getContext() возвращает контекст родительского Activity.

mListener = (ConfirmDialogListener) context;

Эта строка устанавливает вашего слушателя как родителя Activity вызывающего Fragment. Вместо установки mListener на родительское действие, вы должны установить его на вызывающий фрагмент.

Во-первых, вы должны установить целевой фрагмент для 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.

...