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

时间:2022-02-22 21:17:39

What is the "correct" way to return the values to the calling activity from a complex custom dialog - say, text fields, date or time picker, a bunch of radio buttons, etc, plus a "Save" and "Cancel" button?

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

Some of the techniques I've seen on the web include:

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

  • public data members in the Dialog-derived class which can be read by the Activity

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

  • public "get" accessors . . . " . . " . . "

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

  • Launching the dialog with an Intent (as opposed to show() ) plus handlers in the Dialog class which take input from the various controls and bundle them up to be passed back to the Activity so when listener hits "Save" the bundle is passed back using ReturnIntent()

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

  • Listeners in the Activity which process input from the controls that are in the dialog e.g., so the TimePicker or DatePicker's listeners are really in the Activity. In this scheme practically all the work is done in the Activity

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

  • One Listener in the Activity for the "Save" button and then the Activity directly interrogates the controls in the dialog; the Activity dismisses the dialog.

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

...plus more that I've already forgotten.

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

Is there a particular technique that's considered the canonically correct or "best practice" method?

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

6 个解决方案

#1


4  

I'm using following way:

我正在使用以下方式:

  1. All my activities has one and the same parent Activity (let's say ControlActivity). ControlActivity has private volatile Bundle controlBundle; with appropriate getter/setter
  2. 我的所有活动都有一个相同的父Activity(比如说ControlActivity)。 ControlActivity有私有的volatile Bundle controlBundle;使用适当的吸气剂/设定器
  3. When I start dialog, I used to call dialog thru my own method:

    当我开始对话时,我曾经通过自己的方法调用对话框:

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

So each time I know parameters sent to dialog

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

  1. When dialog is about to complete, I'm forming in dialog another Bundle with necessary values and then put them thru my Activity bundle setter:
  2. 当对话框即将完成时,我正在对话框中形成另一个包含必要值的Bundle,然后通过我的Activity bundle setter将它们放入:

((ControlActivity )this.getOwnerActivity).setControlBundle(bundle);

So in the end when dialog finishes I know value "returned" from dialog. I know that it's not like int retCode=this.showMyDialog(); it's a bit more complex, but it's workable.

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

#2


19  

Perhaps I'm mis-understanding your question, but why not just use the built in listener system:

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

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

This is how I've always handled data from dialog boxes.

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

EDIT: Let me give you a more concrete example which will better answer your question. I'm going to steal some sample code from this page, which you should read:

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

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

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

#3


7  

For my MIDI app I needed yes/no/cancel confirmation dialogs, so I first made a general StandardDialog class:

对于我的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();
    }
}

Next, in my main activity I already had a message handler for the UI updates from other threads, so I just added code for processing messages from the dialogs. By using a different dialogId parameter when I instantiate the StandardDialog for various program functions, I can execute the proper code to handle the yes/no/cancel responses to different questions. This idea can be extended for complex custom dialogs by sending a Bundle of data though this is much slower than a simple integer message.

接下来,在我的主要活动中,我已经有来自其他线程的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();
            }
        }
    }
};

Then all I need to do is define the do{Whatever}() methods in the activity. To bring up a dialog, as an example I have a method responding to a "clear recorded MIDI events" button and confirm it as follows:

然后,我需要做的就是在活动中定义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 is defined as a unique integer elsewhere. This method makes a Yes/No dialog pop up in front of the activity, which loses focus until the dialog closes, at which time the activity gets a message with the dialog result. Then the message handler calls the doClearDlgYesClicked() method if the "Yes" button was clicked. (I didn't need a message for the "No" button since no action was needed in that case).

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

Anyway, this method works for me, and makes it easy to pass results back from a dialog.

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

#4


2  

I've pondered over this myself for a while and eventually the most convenient way I found of doing this is breaking up my activity into various methods that represent each unit of control flow. For example, if the activities within my activity are say : load variables from intent, check for some data, process and proceed if available, if not make a background call, wait for user interaction, start another activity.

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

I generally pickup the parts that are common, the first two and the last in this case. I'll wrap the first ones in onCreate() and make a separate one for the last... say startAnotherActivity(Data). You can arrange the middle parts so they'll consist of a checkData(Data) (possibly merged into the onCreate()) which calls either processAvailableData(Data) or performBackgroundTask(Data). The background task will perform an operation in the background and return control to onBackgroundTaskCompleted(OtherData).

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

Now both processAvailableData(Data) and onBackgroundTaskCompleted(OtherData) call the getUserResponse() method which in turn may either call startAnotherActivity(Data) or merge its functions with itself.

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

I feel this approach gives a number of benefits.

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

  1. It helps with the data return issue your question points to by "moving forwards" instead of returning data.
  2. 通过“向前移动”而不是返回数据,它有助于您的问题指向的数据返回问题。
  3. It allows easier addition of new functionality. For instance, if we wanted to give the user more options, we could just call the appropriate method from getUserResponse() which could effect the data that is eventually passed to the next activity.
  4. 它允许更容易地添加新功能。例如,如果我们想给用户提供更多选项,我们可以从getUserResponse()调用适当的方法,这可能会影响最终传递给下一个活动的数据。
  5. It helps avoid needless flow problems (check out the questions relating to finish() and return on SO) when our intuitive assumption is a certain flow and it turns out to be another.
  6. 它有助于避免不必要的流动问题(当我们的直觉假设是某个流程并且结果证明是另一个时,检查与完成()和返回SO相关的问题。
  7. Helps manage variables better so you don't end up having a lot of class level fields to avoid the variable access problems in anonymous inner classes (onClick(), doInBackground(), etc.).
  8. 有助于更好地管理变量,因此您最终不会拥有大量的类级别字段来避免匿名内部类(onClick(),doInBackground()等中的变量访问问题)。

I'm pretty sure having more methods adds some overhead but its probably offset by the flow, reuse and simplicity advantages you get (would love to hear a compilation expert's views on this).

我很确定有更多的方法会增加一些开销,但它可能会被你获得的流量,重用和简单优势所抵消(很想听到编译专家对此的看法)。

#5


1  

I will explain one solution with the example of two fragments. Imagine there is a SimpleFragment which has just one text field to render a date. Then there is a DatePickerFragment which allows to choose a particular date. What I want is that the DatePickerFragment passes the date value back to the calling SimpleFragment whenever the user confirms her selection.

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

SimpleFragment

So, first of all we start the DatePickerFragment from within the 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

In the dialog fragment we prepare to pass back the selected date when the user hits the positive button:

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

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

Back in the requesting fragment we consume what has been passed back by the dialog:

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

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

References to mattpic who gave an excellent answer before.

提到之前给出了优秀答案的mattpic。

#6


1  

After quite a bit of research I settled on a callback interface. My code is as follows:

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

MyFragment.java

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

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

}

#1


4  

I'm using following way:

我正在使用以下方式:

  1. All my activities has one and the same parent Activity (let's say ControlActivity). ControlActivity has private volatile Bundle controlBundle; with appropriate getter/setter
  2. 我的所有活动都有一个相同的父Activity(比如说ControlActivity)。 ControlActivity有私有的volatile Bundle controlBundle;使用适当的吸气剂/设定器
  3. When I start dialog, I used to call dialog thru my own method:

    当我开始对话时,我曾经通过自己的方法调用对话框:

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

So each time I know parameters sent to dialog

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

  1. When dialog is about to complete, I'm forming in dialog another Bundle with necessary values and then put them thru my Activity bundle setter:
  2. 当对话框即将完成时,我正在对话框中形成另一个包含必要值的Bundle,然后通过我的Activity bundle setter将它们放入:

((ControlActivity )this.getOwnerActivity).setControlBundle(bundle);

So in the end when dialog finishes I know value "returned" from dialog. I know that it's not like int retCode=this.showMyDialog(); it's a bit more complex, but it's workable.

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

#2


19  

Perhaps I'm mis-understanding your question, but why not just use the built in listener system:

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

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

This is how I've always handled data from dialog boxes.

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

EDIT: Let me give you a more concrete example which will better answer your question. I'm going to steal some sample code from this page, which you should read:

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

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

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

#3


7  

For my MIDI app I needed yes/no/cancel confirmation dialogs, so I first made a general StandardDialog class:

对于我的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();
    }
}

Next, in my main activity I already had a message handler for the UI updates from other threads, so I just added code for processing messages from the dialogs. By using a different dialogId parameter when I instantiate the StandardDialog for various program functions, I can execute the proper code to handle the yes/no/cancel responses to different questions. This idea can be extended for complex custom dialogs by sending a Bundle of data though this is much slower than a simple integer message.

接下来,在我的主要活动中,我已经有来自其他线程的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();
            }
        }
    }
};

Then all I need to do is define the do{Whatever}() methods in the activity. To bring up a dialog, as an example I have a method responding to a "clear recorded MIDI events" button and confirm it as follows:

然后,我需要做的就是在活动中定义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 is defined as a unique integer elsewhere. This method makes a Yes/No dialog pop up in front of the activity, which loses focus until the dialog closes, at which time the activity gets a message with the dialog result. Then the message handler calls the doClearDlgYesClicked() method if the "Yes" button was clicked. (I didn't need a message for the "No" button since no action was needed in that case).

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

Anyway, this method works for me, and makes it easy to pass results back from a dialog.

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

#4


2  

I've pondered over this myself for a while and eventually the most convenient way I found of doing this is breaking up my activity into various methods that represent each unit of control flow. For example, if the activities within my activity are say : load variables from intent, check for some data, process and proceed if available, if not make a background call, wait for user interaction, start another activity.

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

I generally pickup the parts that are common, the first two and the last in this case. I'll wrap the first ones in onCreate() and make a separate one for the last... say startAnotherActivity(Data). You can arrange the middle parts so they'll consist of a checkData(Data) (possibly merged into the onCreate()) which calls either processAvailableData(Data) or performBackgroundTask(Data). The background task will perform an operation in the background and return control to onBackgroundTaskCompleted(OtherData).

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

Now both processAvailableData(Data) and onBackgroundTaskCompleted(OtherData) call the getUserResponse() method which in turn may either call startAnotherActivity(Data) or merge its functions with itself.

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

I feel this approach gives a number of benefits.

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

  1. It helps with the data return issue your question points to by "moving forwards" instead of returning data.
  2. 通过“向前移动”而不是返回数据,它有助于您的问题指向的数据返回问题。
  3. It allows easier addition of new functionality. For instance, if we wanted to give the user more options, we could just call the appropriate method from getUserResponse() which could effect the data that is eventually passed to the next activity.
  4. 它允许更容易地添加新功能。例如,如果我们想给用户提供更多选项,我们可以从getUserResponse()调用适当的方法,这可能会影响最终传递给下一个活动的数据。
  5. It helps avoid needless flow problems (check out the questions relating to finish() and return on SO) when our intuitive assumption is a certain flow and it turns out to be another.
  6. 它有助于避免不必要的流动问题(当我们的直觉假设是某个流程并且结果证明是另一个时,检查与完成()和返回SO相关的问题。
  7. Helps manage variables better so you don't end up having a lot of class level fields to avoid the variable access problems in anonymous inner classes (onClick(), doInBackground(), etc.).
  8. 有助于更好地管理变量,因此您最终不会拥有大量的类级别字段来避免匿名内部类(onClick(),doInBackground()等中的变量访问问题)。

I'm pretty sure having more methods adds some overhead but its probably offset by the flow, reuse and simplicity advantages you get (would love to hear a compilation expert's views on this).

我很确定有更多的方法会增加一些开销,但它可能会被你获得的流量,重用和简单优势所抵消(很想听到编译专家对此的看法)。

#5


1  

I will explain one solution with the example of two fragments. Imagine there is a SimpleFragment which has just one text field to render a date. Then there is a DatePickerFragment which allows to choose a particular date. What I want is that the DatePickerFragment passes the date value back to the calling SimpleFragment whenever the user confirms her selection.

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

SimpleFragment

So, first of all we start the DatePickerFragment from within the 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

In the dialog fragment we prepare to pass back the selected date when the user hits the positive button:

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

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

Back in the requesting fragment we consume what has been passed back by the dialog:

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

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

References to mattpic who gave an excellent answer before.

提到之前给出了优秀答案的mattpic。

#6


1  

After quite a bit of research I settled on a callback interface. My code is as follows:

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

MyFragment.java

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

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

}