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

时间:2010-12-17 19:07:45

标签: android

从复杂的自定义对话框返回值到调用活动的“正确”方法是什么 - 比如文本字段,日期或时间选择器,一堆单选按钮等,加上“保存”和“取消” “按钮?

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

  • Dialog派生类中的公共数据成员,可以被Activity读取

  • 公共“获取”访问者。 。 。 “......” 。 “

  • 使用Intent(而不是 show())以及Dialog类中的处理程序启动对话框,该处理程序从各种控件中获取输入并将它们捆绑起来以传递回活动所以当监听器点击“保存”时,使用 ReturnIntent()

  • 传回包。
  • Activity中的监听器处理对话框中控件的输入,例如,TimePicker或DatePicker的监听器实际上在Activity中。在这个方案中,几乎所有工作都在活动

  • 中完成
  • 活动中的一个监听器为“保存”按钮,然后活动直接询问对话框中的控件;活动驳回了对话框。

...加上我已经忘记的更多。

是否有一种特殊技术被认为是规范正确或“最佳实践”方法?

6 个答案:

答案 0 :(得分:20)

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

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)
}

答案 1 :(得分:7)

对于我的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更新的消息处理程序,所以我只是添加了用于处理来自对话框的消息的代码。通过在我为各种程序函数实例化StandardDialog时使用不同的dialogId参数,我可以执行正确的代码来处理对不同问题的是/否/取消响应。这个想法可以通过发送数据包扩展到复杂的自定义对话框,虽然这比简单的整数消息慢得多。

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}()方法。为了调出一个对话框,作为一个例子,我有一个响应“清除录制的MIDI事件”按钮的方法并按如下方式确认:

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

clearDlgId被定义为其他地方的唯一整数。此方法在活动前面弹出“是/否”对话框,在对话框关闭之前会丢失焦点,此时活动会收到带有对话框结果的消息。然后,如果单击“是”按钮,则消息处理程序将调用doClearDlgYesClicked()方法。 (我不需要“否”按钮的消息,因为在那种情况下不需要采取任何行动)。

无论如何,这种方法对我有用,并且可以很容易地从对话框中传回结果。

答案 2 :(得分:5)

我正在使用以下方式:

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

    public void showMyDialog(int id, Bundle bundle)
    {
        this.controlBundle=bundle;
        this.showDialog(id, bundle);
    }
    
  3. 所以每次我都知道发送到对话框的参数

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

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

答案 3 :(得分:2)

我自己一直在思考这个问题,最终我发现这样做最方便的方法是将我的活动分解成代表每个控制流单位的各种方法。例如,如果我的活动中的活动是:从意图加载变量,检查一些数据,处理并继续(如果可用),如果不进行后台调用,等待用户交互,启动另一个活动。

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

现在,processAvailableData(Data)onBackgroundTaskCompleted(OtherData)都会调用getUserResponse()方法,而startAnotherActivity(Data)方法可能会调用getUserResponse()或将其功能与自身合并。

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

  1. 通过“向前移动”而不是返回数据,它可以帮助解决您的问题指向的数据返回问题。
  2. 它允许更轻松地添加新功能。例如,如果我们想给用户提供更多选项,我们可以从finish()调用适当的方法,这可能会影响最终传递给下一个活动的数据。
  3. 当我们直观的假设是某个流程并且结果证明是另一个流程时,它有助于避免不必要的流动问题(查看与return和{{1}}有关的问题)。
  4. 帮助更好地管理变量,这样您就不会有很多类级别字段来避免匿名内部类(onClick(),doInBackground()等中的变量访问问题。)。
  5. 我很确定有更多的方法会增加一些开销,但它可能会被你获得的流量,重用和简单优势所抵消(很想听到汇编专家对此的看法)。

答案 4 :(得分:1)

我将用两个片段的例子解释一个解决方案。想象一下,SimpleFragment只有一个文本字段来呈现日期。然后有DatePickerFragment允许选择特定日期。我想要的是,只要用户确认她的选择,DatePickerFragment就会将日期值传递回调用SimpleFragment

SimpleFragment

首先,我们从DatePickerFragment

开始SimpleFragment
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

在对话框片段中,我们准备在用户点击肯定按钮时传回所选日期:

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);
    }
}

之前提供mattpicexcellent answer的引用。

答案 5 :(得分:1)

经过相当多的研究后,我选择了一个回调接口。我的代码如下:

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();
}

}