通过 AlertDialogFragment 中的 Bundle 传递侦听器 - 这可能吗?

2022-09-04 03:42:51

我有一个简单的类:

public class AlertDialogFragment extends DialogFragment {

    private static final DialogInterface.OnClickListener DUMMY_ON_BUTTON_CLICKED_LISTENER = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // do nothing
        }
    };

    public static final class Builder implements Parcelable {

        public static final Creator<Builder> CREATOR = new Creator<Builder>() {
            @Override
            public Builder createFromParcel(Parcel source) {
                return new Builder(source);
            }

            @Override
            public Builder[] newArray(int size) {
                return new Builder[size];
            }
        };

        private Optional<Integer> title;
        private Optional<Integer> message;
        private Optional<Integer> positiveButtonText;
        private Optional<Integer> negativeButtonText;

        public Builder() {
            title = Optional.absent();
            message = Optional.absent();
            positiveButtonText = Optional.absent();
            negativeButtonText = Optional.absent();
        }

        public Builder(Parcel in) {
            title = (Optional<Integer>) in.readSerializable();
            message = (Optional<Integer>) in.readSerializable();
            positiveButtonText = (Optional<Integer>) in.readSerializable();
            negativeButtonText = (Optional<Integer>) in.readSerializable();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            out.writeSerializable(title);
            out.writeSerializable(message);
            out.writeSerializable(positiveButtonText);
            out.writeSerializable(negativeButtonText);
        }

        @Override
        public int describeContents() {
            return 0;
        }

        public Builder withTitle(Integer title) {
            this.title = Optional.fromNullable(title);
            return this;
        }

        public Builder withMessage(Integer message) {
            this.message = Optional.fromNullable(message);
            return this;
        }

        public Builder withPositiveButton(int buttonText) {
            this.positiveButtonText = Optional.fromNullable(buttonText);
            return this;
        }

        public Builder withNegativeButton(int buttonText) {
            this.negativeButtonText = Optional.fromNullable(buttonText);
            return this;
        }

        private void set(AlertDialog.Builder dialogBuilder, final AlertDialogFragment alertDialogFragment) {
            if (title.isPresent()) {
                dialogBuilder.setTitle(title.get());
            }
            if (message.isPresent()) {
                dialogBuilder.setMessage(message.get());
            }
            if (positiveButtonText.isPresent()) {
                dialogBuilder.setPositiveButton(positiveButtonText.get(), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        alertDialogFragment.onPositiveButtonClickedListener.onClick(dialog, which);
                    }
                });
            }
            if (negativeButtonText.isPresent()) {
                dialogBuilder.setNegativeButton(negativeButtonText.get(), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        alertDialogFragment.onNegativeButtonClickedListener.onClick(dialog, which);
                    }
                });
            }
        }

        public AlertDialogFragment build() {
            return AlertDialogFragment.newInstance(this);
        }
    }


    private static final String KEY_BUILDER = "builder";

    private DialogInterface.OnClickListener onPositiveButtonClickedListener = DUMMY_ON_BUTTON_CLICKED_LISTENER;
    private DialogInterface.OnClickListener onNegativeButtonClickedListener = DUMMY_ON_BUTTON_CLICKED_LISTENER;


    private static AlertDialogFragment newInstance(Builder builder) {
        Bundle args = new Bundle();
        args.putParcelable(KEY_BUILDER, builder);
        AlertDialogFragment fragment = new AlertDialogFragment();
        fragment.setArguments(args);
        return fragment;
    }

    public void setOnPositiveButtonClickedListener(DialogInterface.OnClickListener listener) {
        this.onPositiveButtonClickedListener = listener != null ? listener : DUMMY_ON_BUTTON_CLICKED_LISTENER;
    }

    public void setOnNegativeButtonClickedListener(DialogInterface.OnClickListener listener) {
        this.onNegativeButtonClickedListener = listener != null ? listener : DUMMY_ON_BUTTON_CLICKED_LISTENER;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
        Builder builder = getArguments().getParcelable(KEY_BUILDER);
        builder.set(alertDialogBuilder, this);
        return alertDialogBuilder.create();
    }


}

现在我必须设置按钮点击监听器直接进入,因为我无法通过(args)传递监听器。但是我想 - 所以它看起来像实例化一个:SimpleDialogFragmentBundleAlertDialog

AlertDialogFragment dialogFragment = new AlertDialogFragment.Builder()
                .withTitle(R.string.no_internet_connection)
                .withMessage(messageId)
                .withPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                }).build();
dialogFragment.show(getSupportFragmentManager(), FRAGMENT_TAG_NO_INTERNET_CONNECTION);

但现在我应该这样设置侦听器:

AlertDialogFragment dialogFragment = new AlertDialogFragment.Builder()
                .withTitle(R.string.no_internet_connection)
                .withMessage(messageId)
                .withPositiveButton(android.R.string.ok)
                .build();
dialogFragment.setOnPositiveButtonClickListener(new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
dialogFragment.show(getSupportFragmentManager(), FRAGMENT_TAG_NO_INTERNET_CONNECTION);

也许设置按钮单击侦听器直接到实例,而不是通过参数传递它们是不安全的,因为将参数传递给的推荐方法是通过参数传递它们。DialogFragmentBundleFragmentBundle

而且我知道,在Android中与s通信的推荐方法是强制主机活动实现回调接口。但是,这样就不清楚 Activity 是否应该实现此接口,直到在运行时抛出为止。而且它还具有很强的依赖性 - 要在活动之外的某个地方使用它我应该在活动中实现接口。所以我不能在“独立于”主机活动中使用它FragmentClassCastExceptionCallbackFragmentprepareAlertDialogFragment().show(getActivity().getSupportFragmentManager(), "tag");


答案 1

从听起来你希望有一个警报对话框,它可以有自己的侦听器,可以响应按钮按下事件(有点像OnClickListener)。我实现这一目标的方法是创建自定义的DialogFragment以及扩展Parcellable的侦听器。

ConfirmOrCancelDialogFragment.java

这是您的对话实现。它的处理方式与片段非常相似,只是它通过静态方法调用来实例化。newInstance

public class ConfirmOrCancelDialogFragment extends DialogFragment {
    TextView tvDialogHeader,
            tvDialogBody;

    Button bConfirm,
            bCancel;

    private ConfirmOrCancelDialogListener mListener;

    private String mTitle,
            mBody,
            mConfirmButton,
            mCancelButton;

    public ConfirmOrCancelDialogFragment() {
    }

    public static ConfirmOrCancelDialogFragment newInstance(String title, String body, ConfirmOrCancelDialogListener listener) {
        ConfirmOrCancelDialogFragment fragment = new ConfirmOrCancelDialogFragment();
        Bundle args = new Bundle();
        args.putString("title", title);
        args.putString("body", body);
        args.putParcelable("listener", listener);
        fragment.setArguments(args);
        return fragment;
    }

    public static ConfirmOrCancelDialogFragment newInstance(String title, String body, String confirmButton, String cancelButton, ConfirmOrCancelDialogListener listener) {
        ConfirmOrCancelDialogFragment fragment = new ConfirmOrCancelDialogFragment();
        Bundle args = new Bundle();
        args.putString("title", title);
        args.putString("body", body);
        args.putString("confirmButton", confirmButton);
        args.putString("cancelButton", cancelButton);
        args.putParcelable("listener", listener);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.dialog_confirm_or_cancel, container);

        /* Initial Dialog Setup */
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); // we are using a textview for the title
        mListener = getArguments().getParcelable("listener");

        /* Link UI */
        tvDialogHeader = (TextView) view.findViewById(R.id.tvDialogHeader);
        tvDialogBody = (TextView) view.findViewById(R.id.tvDialogBody);
        bConfirm = (Button) view.findViewById(R.id.bConfirm);
        bCancel = (Button) view.findViewById(R.id.bCancel);

        /* Setup UI */
        mTitle = getArguments().getString("title", "");
        mBody = getArguments().getString("body", "");
        mConfirmButton = getArguments().getString("confirmButton", getResources().getString(R.string.yes_delete));
        mCancelButton = getArguments().getString("cancelButton", getResources().getString(R.string.no_do_not_delete));

        tvDialogHeader.setText(mTitle);
        tvDialogBody.setText(mBody);
        bConfirm.setText(mConfirmButton);
        bCancel.setText(mCancelButton);

        bConfirm.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mListener.onConfirmButtonPressed();
                dismiss();
            }
        });

        bCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mListener.onCancelButtonPressed();
                dismiss();
            }
        });

        return view;
    }
}

ConfirmOrCancelDialogListener.java

这是您的侦听器实现。您始终可以添加更多内容,但只需确保它扩展,以便可以通过 中的方法中的捆绑包进行传递即可。ParcelablenewInstanceConfirmOrCancelDialogFragment.java

public interface ConfirmOrCancelDialogListener extends Parcelable {
    void onConfirmButtonPressed();

    void onCancelButtonPressed();
}

使用示例

这就是事情变得比我想要的更混乱的地方。由于您的侦听器正在扩展 ,因此您还必须重写那些方法,即 和 。幸运的是,它们可能大部分是空白的,一切仍然工作正常。ParcelabledescribeContentswriteToParcel

FragmentManager fm = getActivity().getSupportFragmentManager();
ConfirmOrCancelDialogFragment confirmOrCancelDialogFragment = ConfirmOrCancelDialogFragment.newInstance
    (getString(R.string.header), getString(R.string.body),
                        new ConfirmOrCancelDialogListener() {
                            @Override
                            public void onConfirmButtonPressed() {
                                
                            }

                            public void onCancelButtonPressed() {
                            }

                            @Override
                            public int describeContents() {
                                return 0;
                            }

                            @Override
                            public void writeToParcel(Parcel dest, int flags) {
                            }
                        }
                );
confirmOrCancelDialogFragment.show(fm, "fragment_delete_confirmation");

这并不能完全回答您通过传递它们的问题,但我认为如果这个问题已经很长时间没有答案,那么值得举一个如何使用自定义对话框完成任务的示例,这似乎可以让您对样式和功能有更多的控制。AlertDialogFragment


答案 2

如果您需要少量升级的 Kotlin 版本,请点击此处:

class AlertDialogFragment : DialogFragment() {
    companion object {
        internal fun newInstance(
            exampleParameter: String,
            listener: AlertDialogListener? = null
        ) = AlertDialogFragment().apply {
            this.arguments = Bundle().apply {
                this.putString(EXAMPLE_PARAMETER, exampleParameter)
                this.putParcelable(ALERT_LISTENER, listener)
            }
        }

        interface AlertDialogListener: Parcelable {
            fun primaryActionClicked()
            fun secondaryActionClicked() { /* nop */ }
            override fun describeContents(): Int = 0
            override fun writeToParcel(dest: Parcel, flags: Int) { /* nop */ }
        }

        const val EXAMPLE_PARAMETER = "example_parameter"
        const val ALERT_LISTENER = "alert_listener"
    }

    private var listener: AlertDialogListener? = null

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        return inflater.inflate(R.layout.dialog_alert, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        initListeners()
        initExampleParameter()
    }

    private fun initListeners() {
        val listener = arguments!!.getParcelable<AlertDialogListener>(ALERT_LISTENER)
        if (listener != null) {
            this.listener = listener
        }
    }

    private fun initExampleParameter() {
        example_view.text = arguments!!.getString(EXAMPLE_PARAMETER)!!
        example_view.setOnClickListener {
            listener?.primaryActionClicked()
            dismiss()
        }
    }
}

然后你以这种方式启动它:

AlertDialogFragment.newInstance(
            getString(R.string.example_parameter),
            object : AlertDialogFragment.Companion.AlertDialogListener {
                override fun primaryActionClicked() {
                    // DO SOMETHING ABOUT THE ACTION
                }
            }
        ).show(childFragmentManager, AlertDialogFragment::class.java.name)

您可能还希望忽略侦听器参数,以防您只需要通知用户所发生的事情。祝您编码愉快!


推荐