I have a simple class:
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();
}
}
Now I have to set on button click listeners in SimpleDialogFragment
directly, because I can't pass the listeners via Bundle
(args). But I want to - so it would look like instantiating an AlertDialog
:
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);
But now I should set listeners this way:
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);
Perhaps setting on button click listeners directly to DialogFragment
instance, rather than passing them via Bundle
arguments, is not safe, because the recommended way to pass arguments to Fragment
is passing them via Bundle
arguments.
And I know that the recommended way to communicate with Fragment
s in Android is to oblige host activity to implement callback interface. But this way it's not clear that Activity should implement this interface until ClassCastException
will be thrown in runtime. And it also makes strong dependence - to use it somewhere outside Activity I should implement the Callback
interface in Activity. So I cannot use it in Fragment
s "independent" of host Activities: prepareAlertDialogFragment().show(getActivity().getSupportFragmentManager(), "tag");
From what it sounds like you want to have an alert dialog that can have it's own listener which can respond to button press events (kind of like OnClickListener). The way that I have achieved this is by creating a custom DialogFragment along with a listener which extends Parcelable.
This is your dialog implementation. It's treated very similar to fragments except the way it's instantiated which is through a static method call to 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;
}
}
This is your listener implementation. You could always add more to this, but just make sure it extends Parcelable
so it can be passed through the bundle in the newInstance
method found in ConfirmOrCancelDialogFragment.java
.
public interface ConfirmOrCancelDialogListener extends Parcelable {
void onConfirmButtonPressed();
void onCancelButtonPressed();
}
This is where things get a little messier than I would like. Since your listener is extending Parcelable
, you also have to override those methods as well which are describeContents
and writeToParcel
. Luckily, they can be mostly blank and everything still works fine.
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");
This doesn't completely answer your question of passing them in through an AlertDialogFragment
, but I figure if this question has gone unanswered this long, it's worth giving an example of how to accomplish task with a custom Dialog, which seems to give you a little more control over the style and functionality anyway.
In case you needed Kotlin version with few upgrades, here it is:
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()
}
}
}
Then you start it this way:
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)
You might also want to ignore the listener parameter in case you only need to inform user about something that happened. Happy coding!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With