Android“最佳实践”从对话框返回值

什么是从复杂的自定义对话框(比如文本字段,date或时间select器,一堆单选button等等),再加上“保存”和“取消”button,将值返回给调用活动的“正确”方法?

我在网上看到的一些技巧包括:

  • 对话框派生类中的公共数据成员可以被Activity读取

  • 公众“获取”访问者。 。 。 “”。 。 “

  • 在Dialog类中使用Intent(而不是show() )和处理程序启动对话框,该类从各种控件获取input,并将其捆绑到活动中,以便在监听器点击“保存”时传回使用ReturnIntent()

  • 活动中的监听器处理来自对话框中的控件的input,例如,所以TimePicker或DatePicker的监听器确实在活动中。 在这个计划中,几乎所有的工作都在活动中完成

  • 一个监听器在“保存”button的Activity中,然后Activity直接询问对话框中的控件; 该活动closures对话框。

再加上我已经忘记了

有没有一种特定的技术被认为是正确的或“最佳实践”的方法?

我正在使用以下方式:

  1. 我所有的活动都有一个同样的父活动(比方说ControlActivity)。 ControlActivity具有private volatile Bundle controlBundle; 与适当的getter / setter
  2. 当我开始对话时,我曾经通过我自己的方法调用对话框:

     public void showMyDialog(int id, Bundle bundle) { this.controlBundle=bundle; this.showDialog(id, bundle); } 

所以每次我知道参数发送到对话框

  1. 当对话框即将完成时,我正在对话框中形成另一个必要值的Bundle ,然后通过我的Activity捆绑器设置它们:
 ((ControlActivity )this.getOwnerActivity).setControlBundle(bundle); 

所以最后当对话结束时,我知道从对话框“返回”值。 我知道它不像int retCode=this.showMyDialog(); 这有点复杂,但是可行。

也许我错误理解你的问题,但为什么不使用内置的监听器系统:

 builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // run whatever code you want to run here // if you need to pass data back, just call a function in your // activity and pass it some parameters } }) 

这就是我一直处理对话框中的数据的方式。

编辑:让我给你一个更具体的例子,这将更好地回答你的问题。 我要从这个页面上偷取一些示例代码,你应该阅读:

http://developer.android.com/guide/topics/ui/dialogs.html

 // Alert Dialog code (mostly copied from the Android docs AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Pick a color"); builder.setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { myFunction(item); } }); AlertDialog alert = builder.create(); 

 // Now elsewhere in your Activity class, you would have this function private void myFunction(int result){ // Now the data has been "returned" (as pointed out, that's not // the right terminology) } 

对于我的MIDI应用程序,我需要是/否/取消确认对话框,所以我首先制作了一个普通的StandardDialog类:

 public class StandardDialog { import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Handler; public class StandardDialog { public static final int dlgResultOk = 0; public static final int dlgResultYes = 1; public static final int dlgResultNo = 2; public static final int dlgResultCancel = 3; public static final int dlgTypeOk = 10; public static final int dlgTypeYesNo = 11; public static final int dlgTypeYesNoCancel = 12; private Handler mResponseHandler; private AlertDialog.Builder mDialogBuilder; private int mDialogId; public StandardDialog(Activity parent, Handler reponseHandler, String title, String message, int dialogType, int dialogId) { mResponseHandler = reponseHandler; mDialogId = dialogId; mDialogBuilder = new AlertDialog.Builder(parent); mDialogBuilder.setCancelable(false); mDialogBuilder.setTitle(title); mDialogBuilder.setIcon(android.R.drawable.ic_dialog_alert); mDialogBuilder.setMessage(message); switch (dialogType) { case dlgTypeOk: mDialogBuilder.setNeutralButton("Ok", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mResponseHandler.sendEmptyMessage(mDialogId + dlgResultOk); } }); break; case dlgTypeYesNo: case dlgTypeYesNoCancel: mDialogBuilder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mResponseHandler.sendEmptyMessage(mDialogId + dlgResultYes); } }); mDialogBuilder.setNegativeButton("No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mResponseHandler.sendEmptyMessage(mDialogId + dlgResultNo); } }); if (dialogType == dlgTypeYesNoCancel) { mDialogBuilder.setNeutralButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mResponseHandler.sendEmptyMessage(mDialogId + dlgResultCancel); } }); } break; } mDialogBuilder.show(); } } 

接下来,在我的主要活动中,我已经有了来自其他线程的UI更新的消息处理程序,所以我只是添加了用于处理来自对话的消息的代码。 当我为不同的程序function实例化StandardDialog时,通过使用不同的dialogId参数,我可以执行正确的代码来处理针对不同问题的是/否/取消响应。 这个想法可以通过发送一个Bundle数据来扩展复杂的自定义对话框,尽pipe这比一个简单的整数消息慢得多。

 private Handler uiMsgHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg != null) { // {Code to check for other UI messages here} // Check for dialog box responses if (msg.what == (clearDlgId + StandardDialog.dlgResultYes)) { doClearDlgYesClicked(); } else if (msg.what == (recordDlgId + StandardDialog.dlgResultYes)) { doRecordDlgYesClicked(); } else if (msg.what == (recordDlgId + StandardDialog.dlgResultNo)) { doRecordDlgNoClicked(); } } } }; 

然后,我需要做的是在活动中定义do {Whatever}()方法。 举一个对话框,作为一个例子,我有一个方法响应“清除logging的MIDI事件”button并按如下方式确认:

 public void onClearBtnClicked(View view) { new StandardDialog(this, uiMsgHandler, getResources().getString(R.string.dlgTitleClear), getResources().getString(R.string.dlgMsgClear), StandardDialog.dlgTypeYesNo, clearDlgId); } 

clearDlgId被定义为别处的唯一整数。 此方法会在活动前popup一个“是/否”对话框,在对话框closures之前会失去焦点,此时活动将获得带有对话结果的消息。 然后,如果单击“是”button,消息处理程序会调用doClearDlgYesClicked()方法。 (我不需要“No”button的消息,因为在这种情况下不需要操作)。

无论如何,这个方法适用于我,并且很容易从对话中传回结果。

我一直在思考这个问题,最终我find的最方便的方法是将我的活动分解成代表每个控制stream单元的各种方法。 例如,如果我的活动中的活动是说:从意图加载variables,检查一些数据,处理和继续(如果不可用),如果不做一个后台调用,等待用户交互,启动另一个活动。

我通常在这种情况下拾取常见的部分,前两部分和最后一部分。 我将包装在onCreate()的第一个,并为最后一个单独的…说startAnotherActivity(Data) 。 您可以安排中间部分,以便它们包含checkData(Data) (可能合并到onCreate() ),它调用processAvailableData(Data)performBackgroundTask(Data) 。 后台任务将在后台执行一个操作,并将控制权返回给onBackgroundTaskCompleted(OtherData)

现在, processAvailableData(Data)onBackgroundTaskCompleted(OtherData)调用getUserResponse()方法,该方法可以调用startAnotherActivity(Data)或将其函数与自身合并。

我觉得这种方法有很多好处。

  1. 它可以帮助您通过“向前移动”而不是返回数据来回答您的问题。
  2. 它允许更容易地添加新的function。 例如,如果我们想给用户更多的select,我们可以从getUserResponse()调用适当的方法,这会影响最终传递给下一个活动的数据。
  3. 它有助于避免不必要的stream程问题(当我们的直觉假设是一定的stream程,并且变成另一个stream程时,检查与finish()return的问题有关finish()
  4. 有助于更好地pipe理variables,所以最终不会有大量的类级别字段来避免匿名内部类(onClick(),doInBackground()等)中的variables访问问题。

我很肯定有更多的方法增加了一些开销,但它可能抵消stream量,重用和简单的优势,你会(听到汇编专家的意见)。

我将用两个片段的例子来解释一个解决scheme。 想象一下,有一个SimpleFragment只有一个文本字段来呈现date。 然后有一个DatePickerFragment允许select一个特定的date。 我想要的是DatePickerFragment将date值传递给调用SimpleFragment每当用户确认她的select。

SimpleFragment

所以,首先我们从SimpleFragment启动DatePickerFragment

 private DateTime mFavoriteDate; // Joda-Time date private void launchDatePicker() { DatePickerFragment datePickerFragment = new DatePickerFragment(); Bundle extras = new Bundle(); // Pass an initial or the last value for the date picker long dateInMilliSeconds = mFavoriteDate.getMillis(); extras.putLong(BundleKeys.LAST_KNOWN_DATE, dateInMilliSeconds); datePickerFragment.setArguments(extras); datePickerFragment.setTargetFragment(this, SIMPLE_FRAGMENT_REQUEST_CODE); datePickerFragment.show(getActivity().getSupportFragmentManager(), DatePickerFragment.FRAGMENT_TAG); } 

DatePickerFragment

在对话框片段中,我们准备在用户点击“肯定”button时返回所选date:

 public static final String DATE_PICKED_INTENT_KEY = "DATE_PICKED_INTENT_KEY"; public static final int DATE_PICKED_RESULT_CODE = 123; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // ... Long dateInMilliSeconds = getArguments().getLong(BundleKeys.LAST_KNOWN_DATE); DateTime date = new DateTime(dateInMilliSeconds); initializePickerUiControl(date); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity); dialogBuilder .setPositiveButton(R.string.date_picker_positive, (dialog, which) -> { // Pass date to caller passBackDate(); }) .setNegativeButton(R.string.date_picker_negative, (dialog, which) -> { // Nothing to do here }); return dialogBuilder.create(); } private void passBackDate() { DateTime dateTime = getDateTimeFromPickerControl(); Intent intent = new Intent(); intent.putExtra(DATE_PICKED_INTENT_KEY, dateTime.getMillis()); getTargetFragment().onActivityResult( getTargetRequestCode(), DATE_PICKED_RESULT_CODE, intent); } 

SimpleFragment

回到请求片段中,我们使用对话框传回的内容:

 @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SIMPLE_FRAGMENT_REQUEST_CODE && resultCode == DatePickerFragment.DATE_PICKED_RESULT_CODE) { long datePickedInMilliseconds = data.getLongExtra( DatePickerFragment.DATE_PICKED_INTENT_KEY, 0); mFavoriteDate = new DateTime(datePickedInMilliseconds); updateFavoriteDateTextView(); } else { super.onActivityResult(requestCode, resultCode, data); } } 

之前给出了一个很好的答案的参考。

经过相当多的研究后,我决定在一个callback界面上。 我的代码如下:

MyFragment.java

 public class MyFragment extends Fragment { 

 private void displayFilter() { FragmentManager fragmentManager = getFragmentManager(); FilterDialogFragment filterDialogFragment = new FilterDialogFragment(); Bundle bundle = new Bundle(); bundle.putSerializable("listener", new FilterDialogFragment.OnFilterClickListener() { @Override public void onFilterClickListener() { System.out.println("LISTENER CLICKED"); } }); filterDialogFragment.setArguments(bundle); filterDialogFragment.show(fragmentManager, DIALOG_FILTER); } 

MyDialog.java

 public class MyDialog extends DialogFragment { private ImageButton mBtnTest; private OnFilterClickListener mOnFilterClickListener; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // Get the layout inflater LayoutInflater inflater = getActivity().getLayoutInflater(); View filterLayout = inflater.inflate(R.layout.filter_dialog, null); // Inflate and set the layout for the dialog // Pass null as the parent view because its going in the dialog layout builder.setView(filterLayout) .setTitle("Filter"); Dialog dialog = builder.create(); mOnFilterClickListener = (OnFilterClickListener) getArguments().getSerializable("listener"); mBtnTest = (ImageButton)filterLayout.findViewById(R.id.fandb); mBtnTest.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { mOnFilterClickListener.onFilterClickListener(); dismiss(); } }); return dialog; } public interface OnFilterClickListener extends Serializable { void onFilterClickListener(); } }