Android DialogFragment vs Dialog
Google建议我们使用
是的,使用
关于处理片段中的事件,有各种方法可以做到,但我只是在我的
在对话框中保存一条消息并在构造函数中实例化它:
1 2 3 | private Message okMessage; ... okMessage = handler.obtainMessage(MY_MSG_WHAT, MY_MSG_OK); |
在对话框中实现
1 2 3 4 5 6 | public void onClick(..... if (which == DialogInterface.BUTTON_POSITIVE) { final Message toSend = Message.obtain(okMessage); toSend.sendToTarget(); } } |
编辑
由于
1 | outState.putParcelable("okMessage", okMessage); |
然后在
1 2 3 | if (savedInstanceState != null) { okMessage = savedInstanceState.getParcelable("okMessage"); } |
您可以创建一般的DialogFragment子类,如YesNoDialog和OkDialog,如果您在应用程序中使用了很多对话框,则传入标题和消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class YesNoDialog extends DialogFragment { public static final String ARG_TITLE ="YesNoDialog.Title"; public static final String ARG_MESSAGE ="YesNoDialog.Message"; public YesNoDialog() { } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Bundle args = getArguments(); String title = args.getString(ARG_TITLE); String message = args.getString(ARG_MESSAGE); return new AlertDialog.Builder(getActivity()) .setTitle(title) .setMessage(message) .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null); } }) .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, null); } }) .create(); } } |
然后使用以下方法调用它:
1 2 3 4 5 6 7 | DialogFragment dialog = new YesNoDialog(); Bundle args = new Bundle(); args.putString(YesNoDialog.ARG_TITLE, title); args.putString(YesNoDialog.ARG_MESSAGE, message); dialog.setArguments(args); dialog.setTargetFragment(this, YES_NO_CALL); dialog.show(getFragmentManager(),"tag"); |
并在
在AlertDialog上使用DialogFragment:
-
自API级别13引入以来:
不推荐使用Activity中的showDialog方法。
不建议在代码中的其他位置调用对话框,因为您必须自己管理对话框(例如方向更改)。 -
差异DialogFragment - AlertDialog
他们有这么大的不同吗?从关于DialogFragment的Android参考:
A DialogFragment is a fragment that displays a dialog window, floating on top of its
activity's window. This fragment contains a Dialog object, which it
displays as appropriate based on the fragment's state. Control of the
dialog (deciding when to show, hide, dismiss it) should be done
through the API here, not with direct calls on the dialog. -
其他说明
- 由于具有不同屏幕尺寸的设备的多样性,片段是Android框架中的自然演变。
- DialogFragments和Fragments在支持库中提供,使该类可用于所有当前使用的Android版本。
我建议使用
当然,使用它创建一个"是/否"对话框非常复杂,因为它应该是相当简单的任务,但是创建一个带有
(活动生命周期使其变得复杂 - 您必须让
好处是你通常可以很容易地在
使用Builder Pattern的通用AlertDialogFragment
在我的项目中,在我发现它存在问题之前,我已经使用了
因此,我结合了不同的方法来创建一个通用的
解
(请注意,我在我的代码中使用Java 8 lambda表达式,因此如果您还没有使用lambda表达式,则可能必须更改部分代码。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | /** * Helper class for dialog fragments to show a {@link AlertDialog}. It can be used almost exactly * like a {@link AlertDialog.Builder} * <p /> * Creation Date: 22.03.16 * * @author felix, http://flx-apps.com/ */ public class AlertDialogFragment extends DialogFragment { protected FragmentActivity activity; protected Bundle args; protected String tag = AlertDialogFragment.class.getSimpleName(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); activity = getActivity(); args = getArguments(); } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Dialog dialog = setDialogDefaults(new AlertDialog.Builder(getActivity())).create(); if (args.containsKey("gravity")) { dialog.getWindow().getAttributes().gravity = args.getInt("gravity"); } dialog.setOnShowListener(d -> { if (dialog != null && dialog.findViewById((android.R.id.message)) != null) { ((TextView) dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance()); } }); return dialog; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); if (args.containsKey("onDismissListener")) { Parcelable onDismissListener = args.getParcelable("onDismissListener"); if (onDismissListener != null && onDismissListener instanceof ParcelableOnDismissListener) { ((ParcelableOnDismissListener) onDismissListener).onDismiss(this); } } } /** * Sets default dialog properties by arguments which were set using {@link #builder(FragmentActivity)} */ protected AlertDialog.Builder setDialogDefaults(AlertDialog.Builder builder) { args = getArguments(); activity = getActivity(); if (args.containsKey("title")) { builder.setTitle(args.getCharSequence("title")); } if (args.containsKey("message")) { CharSequence message = args.getCharSequence("message"); builder.setMessage(message); } if (args.containsKey("viewId")) { builder.setView(getActivity().getLayoutInflater().inflate(args.getInt("viewId"), null)); } if (args.containsKey("positiveButtonText")) { builder.setPositiveButton(args.getCharSequence("positiveButtonText"), (dialog, which) -> { onButtonClicked("positiveButtonListener", which); }); } if (args.containsKey("negativeButtonText")) { builder.setNegativeButton(args.getCharSequence("negativeButtonText"), (dialog, which) -> { onButtonClicked("negativeButtonListener", which); }); } if (args.containsKey("neutralButtonText")) { builder.setNeutralButton(args.getCharSequence("neutralButtonText"), (dialog, which) -> { onButtonClicked("neutralButtonListener", which); }); } if (args.containsKey("items")) { builder.setItems(args.getStringArray("items"), (dialog, which) -> { onButtonClicked("itemClickListener", which); }); } // @formatter:off // FIXME this a pretty hacky workaround: we don't want to show the dialog if onClickListener of one of the dialog's button click listener were lost // the problem is, that there is no (known) solution for parceling a OnClickListener in the long term (only for state changes like orientation change, // but not if the Activity was completely lost) if ( (args.getParcelable("positiveButtonListener") != null && !(args.getParcelable("positiveButtonListener") instanceof ParcelableOnClickListener)) || (args.getParcelable("negativeButtonListener") != null && !(args.getParcelable("negativeButtonListener") instanceof ParcelableOnClickListener)) || (args.getParcelable("neutralButtonListener") != null && !(args.getParcelable("neutralButtonListener") instanceof ParcelableOnClickListener)) || (args.getParcelable("itemClickListener") != null && !(args.getParcelable("itemClickListener") instanceof ParcelableOnClickListener)) ) { new DebugMessage("Forgot onClickListener. Needs to be dismissed.") .logLevel(DebugMessage.LogLevel.VERBOSE) .show(); try { dismissAllowingStateLoss(); } catch (NullPointerException | IllegalStateException ignored) {} } // @formatter:on return builder; } public interface OnDismissListener { void onDismiss(AlertDialogFragment dialogFragment); } public interface OnClickListener { void onClick(AlertDialogFragment dialogFragment, int which); } protected void onButtonClicked(String buttonKey, int which) { ParcelableOnClickListener parcelableOnClickListener = getArguments().getParcelable(buttonKey); if (parcelableOnClickListener != null) { parcelableOnClickListener.onClick(this, which); } } // region Convenience Builder Pattern class almost similar to AlertDialog.Builder // ============================================================================================= public AlertDialogFragment builder(FragmentActivity activity) { this.activity = activity; this.args = new Bundle(); return this; } public AlertDialogFragment addArguments(Bundle bundle) { args.putAll(bundle); return this; } public AlertDialogFragment setTitle(int titleStringId) { return setTitle(activity.getString(titleStringId)); } public AlertDialogFragment setTitle(CharSequence title) { args.putCharSequence("title", title); return this; } public AlertDialogFragment setMessage(int messageStringId) { return setMessage(activity.getString(messageStringId)); } public AlertDialogFragment setMessage(CharSequence message) { args.putCharSequence("message", message); return this; } public AlertDialogFragment setPositiveButton(int textStringId, OnClickListener onClickListener) { return setPositiveButton(activity.getString(textStringId), onClickListener); } public AlertDialogFragment setPositiveButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) { args.putCharSequence("positiveButtonText", text); args.putParcelable("positiveButtonListener", createParcelableOnClickListener(onClickListener)); return this; } public AlertDialogFragment setNegativeButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) { return setNegativeButton(activity.getString(textStringId), onClickListener); } public AlertDialogFragment setNegativeButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) { args.putCharSequence("negativeButtonText", text); args.putParcelable("negativeButtonListener", createParcelableOnClickListener(onClickListener)); return this; } public AlertDialogFragment setNeutralButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) { return setNeutralButton(activity.getString(textStringId), onClickListener); } public AlertDialogFragment setNeutralButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) { args.putCharSequence("neutralButtonText", text); args.putParcelable("neutralButtonListener", createParcelableOnClickListener(onClickListener)); return this; } public AlertDialogFragment setOnDismissListener(OnDismissListener onDismissListener) { if (onDismissListener == null) { return this; } Parcelable p = new ParcelableOnDismissListener() { @Override public void onDismiss(AlertDialogFragment dialogFragment) { onDismissListener.onDismiss(dialogFragment); } }; args.putParcelable("onDismissListener", p); return this; } public AlertDialogFragment setItems(String[] items, AlertDialogFragment.OnClickListener onClickListener) { args.putStringArray("items", items); args.putParcelable("itemClickListener", createParcelableOnClickListener(onClickListener)); return this; } public AlertDialogFragment setView(int viewId) { args.putInt("viewId", viewId); return this; } public AlertDialogFragment setGravity(int gravity) { args.putInt("gravity", gravity); return this; } public AlertDialogFragment setTag(String tag) { this.tag = tag; return this; } public AlertDialogFragment create() { setArguments(args); return AlertDialogFragment.this; } public AlertDialogFragment show() { create(); try { super.show(activity.getSupportFragmentManager(), tag); } catch (IllegalStateException e1) { /** * this whole part is used in order to attempt to show the dialog if an * {@link IllegalStateException} was thrown (it's kinda comparable to * {@link FragmentTransaction#commitAllowingStateLoss()} * So you can remove all those dirty hacks if you are sure that you are always * properly showing dialogs in the right moments */ new DebugMessage("got IllegalStateException attempting to show dialog. trying to hack around.") .logLevel(DebugMessage.LogLevel.WARN) .exception(e1) .show(); try { Field mShownByMe = DialogFragment.class.getDeclaredField("mShownByMe"); mShownByMe.setAccessible(true); mShownByMe.set(this, true); Field mDismissed = DialogFragment.class.getDeclaredField("mDismissed"); mDismissed.setAccessible(true); mDismissed.set(this, false); } catch (Exception e2) { new DebugMessage("error while showing dialog") .exception(e2) .logLevel(DebugMessage.LogLevel.ERROR) .show(); } FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction(); transaction.add(this, tag); transaction.commitAllowingStateLoss(); // FIXME hacky and unpredictable workaround } return AlertDialogFragment.this; } @Override public int show(FragmentTransaction transaction, String tag) { throw new NoSuchMethodError("Please use AlertDialogFragment.show()!"); } @Override public void show(FragmentManager manager, String tag) { throw new NoSuchMethodError("Please use AlertDialogFragment.show()!"); } protected ParcelableOnClickListener createParcelableOnClickListener(AlertDialogFragment.OnClickListener onClickListener) { if (onClickListener == null) { return null; } return new ParcelableOnClickListener() { @Override public void onClick(AlertDialogFragment dialogFragment, int which) { onClickListener.onClick(dialogFragment, which); } }; } /** * Parcelable OnClickListener (can be remembered on screen rotation) */ public abstract static class ParcelableOnClickListener extends ResultReceiver implements AlertDialogFragment.OnClickListener { public static final Creator<ResultReceiver> CREATOR = ResultReceiver.CREATOR; ParcelableOnClickListener() { super(null); } @Override public abstract void onClick(AlertDialogFragment dialogFragment, int which); } /** * Parcelable OnDismissListener (can be remembered on screen rotation) */ public abstract static class ParcelableOnDismissListener extends ResultReceiver implements AlertDialogFragment.OnDismissListener { public static final Creator<ResultReceiver> CREATOR = ResultReceiver.CREATOR; ParcelableOnDismissListener() { super(null); } @Override public abstract void onDismiss(AlertDialogFragment dialogFragment); } // ============================================================================================= // endregion } |
用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // showing a normal alert dialog with state loss on configuration changes (like device rotation) new AlertDialog.Builder(getActivity()) .setTitle("Are you sure? (1)") .setMessage("Do you really want to do this?") .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(),"Yes clicked", Toast.LENGTH_SHORT).show()) .setNegativeButton("Cancel", null) .show(); // showing a dialog fragment using the helper class with no state loss on configuration changes new AlertDialogFragment.builder(getActivity()) .setTitle("Are you sure? (2)") .setMessage("Do you really want to do this?") .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(),"Yes clicked", Toast.LENGTH_SHORT).show()) .setNegativeButton("Cancel", null) .show(); |
我在这里发布这个不仅是为了分享我的解决方案,还因为我想问你的意见:这种方法在某种程度上是合法的还是有问题的?
我可以建议对@ ashishduh的答案进行一点简化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class AlertDialogFragment extends DialogFragment { public static final String ARG_TITLE ="AlertDialog.Title"; public static final String ARG_MESSAGE ="AlertDialog.Message"; public static void showAlert(String title, String message, Fragment targetFragment) { DialogFragment dialog = new AlertDialogFragment(); Bundle args = new Bundle(); args.putString(ARG_TITLE, title); args.putString(ARG_MESSAGE, message); dialog.setArguments(args); dialog.setTargetFragment(targetFragment, 0); dialog.show(targetFragment.getFragmentManager(),"tag"); } public AlertDialogFragment() {} @NonNull @Override public AlertDialog onCreateDialog(Bundle savedInstanceState) { Bundle args = getArguments(); String title = args.getString(ARG_TITLE,""); String message = args.getString(ARG_MESSAGE,""); return new AlertDialog.Builder(getActivity()) .setTitle(title) .setMessage(message) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null); } }) .create(); } |
它消除了(类的)用户熟悉组件内部的需要,并且使用非常简单:
1 | AlertDialogFragment.showAlert(title, message, this); |
附:在我的情况下,我需要一个简单的警报对话框,这就是我创建的内容。您可以将该方法应用于是/否或您需要的任何其他类型。
使用Dialog可以显示简单的是或否对话框。
当您需要更复杂的视图时,您需要掌握生命周期,例如oncreate,请求权限,任何生命周期覆盖,我会使用对话框片段。 因此,您可以将对象需要操作的权限和任何其他代码分开,而无需与调用活动进行通信。