Android Dialogs / AlertDialogs:如何在对话框启动时“阻止执行”(.NET 风格)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2028697/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 04:16:09  来源:igfitidea点击:

Dialogs / AlertDialogs: How to "block execution" while dialog is up (.NET-style)

android

提问by Ted

Coming from the .NET-environment, I'm now looking to understand how Dialogs work in Android.

来自 .NET 环境,我现在想了解 Dialogs 在 Android 中是如何工作的。

In .NET, when calling MessageBox.Show(...)that creates and shows a popup dialog. In the call to Show I can specify what buttons should be available in the popup, for example:

在 .NET 中,调用时MessageBox.Show(...)会创建并显示一个弹出对话框。在对 Show 的调用中,我可以指定弹出窗口中应该提供哪些按钮,例如:

DialogResult myDialogResult = MessageBox.Show("My text here", "My caption here", MessageBoxButtons.YesNoCancel);

As you can see, the call to Show returns a DialogResult when a button is pressed in the popup, informing me what button was clicked. Note that in .NET, execution is halted at the line where the call to Show(...)is made, so it can return the value when a button is pressed.

如您所见,当在弹出窗口中按下按钮时,对 Show 的调用返回一个 DialogResult,通知我单击了哪个按钮。请注意,在 .NET 中,执行会在调用 的行停止Show(...),因此可以在按下按钮时返回值。

If I in the above example press "No" the myDialogResult will be equal to

如果我在上面的示例中按“否”,则 myDialogResult 将等于

myDialogResult == DialogResult.No

Since I find the .NET-way of using/creating popups very easy and intuitive, I would like that way of creating popups in Android too.

由于我发现使用/创建弹出窗口的 .NET 方式非常简单直观,因此我也希望在 Android 中使用这种方式创建弹出窗口。

So, the question is if anyone know how to "halt execution" like with the MessageBox.Show, and then return a value whenever the Button is pressed (and the dialog goes away)?

所以,问题是是否有人知道如何像 一样“停止执行” MessageBox.Show,然后在按下 Button 时返回一个值(并且对话框消失)?

Edit 1

编辑 1

To be a little bit more clear:

更清楚一点:

I need for the execution to halt and wait until the user has chosen a button to click in the popup. The code that follow the call to show the Dialog is dependent on what button is clicked in the Dialog.

我需要停止执行并等待用户选择一个按钮来单击弹出窗口。调用后显示对话框的代码取决于在对话框中单击的按钮。

That's why I cannot use what Erich and Alex suggest, since writing code in the onClick-methods as suggested below is not going to work. The reason is that I cannot continue the "normal execution". Let me take an example:

这就是为什么我不能使用 Erich 和 Alex 建议的原因,因为按照下面的建议在 onClick 方法中编写代码是行不通的。原因是我无法继续“正常执行”。让我举个例子:

Let me take an example:

让我举个例子:

int nextStep = 0; // this variable will not be reached from within the onClick-methods

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Hello!")
       .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                nextStep = 1; // *** COMPILER ERROR!! ***
            }
        })
        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                nextStep = 2; // *** COMPILER ERROR!! ***
            }
        })
        .create().show();

if (nextStep == 1)
{
    // then do some damage
}
else if (nextStep == 2
    // dont do damage

If I wanted the execution to be dependent on the choice in the popup, I would somehow have to make all the variables in the "normal execution" (in this case nextStep) available in the onClick-methods, and that sounds like hell to me.

如果我希望执行依赖于弹出窗口中的选择,我将不得不以某种方式使“正常执行”(在本例中nextStep)中的所有变量在onClick 方法中可用,这对我来说听起来像是地狱。

Edit 2

编辑 2

Another obvious example would be a popup asking "Do you want to continue"with the options "Yes"and "No".

另一个明显的例子是一个弹出窗口,询问“你想继续吗”和选项“是”“否”

If the user presses "Yes", the whole method should be aborted otherwise it should continue execution. How do you solve that nicely?

如果用户按“是”,则应中止整个方法,否则应继续执行。你是怎么解决这个问题的?

采纳答案by Romain Guy

Ted, you don't want to do this, really :) The biggest reason is that if you block the UI thread while you are displaying a Dialog, you will block the thread that's in charge of drawing and handling the events of your Dialog. Which means your dialog will be unresponsive. You will also cause ANRs if the user takes more than a few seconds to click the dialog.

Ted,你不想这样做,真的:) 最大的原因是,如果你在显示对话框时阻塞了 UI 线程,你将阻塞负责绘制和处理对话框事件的线程。这意味着您的对话将无响应。如果用户点击对话框的时间超过几秒钟,您也会导致 ANR。

Erich's answer is exactly what you need. I know it's not what you want, but that doesn't matter. We've designed Android to prevent developers from writing synchronous dialogs so you don't really have much of a choice.

Erich 的回答正是您所需要的。我知道这不是你想要的,但这没关系。我们设计了 Android 以防止开发人员编写同步对话框,因此您实际上没有太多选择。

回答by Erich Douglass

In Android, the structure is different from .NET:

在Android中,结构与.NET不同:

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Hello!")
       .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
               // Handle Ok
           }
       })
       .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
               // Handle Cancel
           }
       })
       .create();

Will get you a dialog with two buttons and you handle the button clicks with callbacks. You might be able to write some code to make the syntax more closely resemble .NET, but the dialog lifecycle is pretty intertwined with Activity, so in the end, it might be more trouble than it's worth. Additional dialog references are here.

将为您提供一个带有两个按钮的对话框,您可以通过回调处理按钮点击。您也许可以编写一些代码使语法更接近 .NET,但对话框生命周期与Activity. 其他对话框参考在这里

回答by MindSpiker

A simplified version of Daniel's answer above. This function gets a yes or no from user in an alert dialog but could easily be modified to get other input.

上面丹尼尔回答的简化版本。这个函数在警告对话框中从用户那里得到是或否,但可以很容易地修改以获得其他输入。

private boolean mResult;
public boolean getYesNoWithExecutionStop(String title, String message, Context context) {
    // make a handler that throws a runtime exception when a message is received
    final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message mesg) {
            throw new RuntimeException();
        } 
    };

    // make a text input dialog and show it
    AlertDialog.Builder alert = new AlertDialog.Builder(context);
    alert.setTitle(title);
    alert.setMessage(message);
    alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton) {
            mResult = true;
            handler.sendMessage(handler.obtainMessage());
        }
    });
    alert.setNegativeButton("No", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton) {
            mResult = false;
            handler.sendMessage(handler.obtainMessage());
        }
    });
    alert.show();

    // loop till a runtime exception is triggered.
    try { Looper.loop(); }
    catch(RuntimeException e2) {}

    return mResult;
}

回答by Dave Webb

In Android Dialogs are asynchronous so you're going to have to structure your code a little differently.

在 Android 中,对话框是异步的,因此您将不得不以稍微不同的方式构建代码。

So in C# your logic ran something like this in pseudocode:

所以在 C# 中,你的逻辑在伪代码中运行如下:

void doSomeStuff() {
    int result = showDialog("Pick Yes or No");

    if (result == YES) {
        //do stuff for yes
    }
    else if (result == NO) {
        //do stuff for no
    }

    //finish off here
}

For Android it's going to have to be less neat. Think of it like so. You'll have an OnClickListenerlike this:

对于Android,它必须不那么整洁。这么想吧。你会有OnClickListener这样的:

public void onClick(DialogInterface dialog, int whichButton) {
   if (whichButton == BUTTON_POSITIVE) {
      doOptionYes();
   }
   else if (whichButton == BUTTON_NEGATIVE) {
      doOptionNo();
   }
}

Which is then supported by the following methods:

然后由以下方法支持:

void doOptionYes() {
    //do stuff for yes
    endThings();
}

void doOptionNo() {
    //do stuff for no
    endThings();
}

void endThings() {
    //clean up here
}

So what was one method is now four. It may not seem as neat but that's how it works I'm afraid.

所以以前的一种方法现在是四种。它可能看起来不那么整洁,但我担心它就是这样工作的。

回答by Daniel

PasswordDialog dlg = new PasswordDialog(this);

if(dlg.showDialog() == DialogResult.OK)

{

    //blabla, anything your self

}


public class PasswordDialog extends Dialog
{
    int dialogResult;
    Handler mHandler ;

    public PasswordDialog(Activity context, String mailName, boolean retry)
    {

        super(context);
        setOwnerActivity(context);
        onCreate();
        TextView promptLbl = (TextView) findViewById(R.id.promptLbl);
        promptLbl.setText("Input password/n" + mailName);
    }
    public int getDialogResult()
    {
        return dialogResult;
    }
    public void setDialogResult(int dialogResult)
    {
        this.dialogResult = dialogResult;
    }
    /** Called when the activity is first created. */

    public void onCreate() {
        setContentView(R.layout.password_dialog);
        findViewById(R.id.cancelBtn).setOnClickListener(new android.view.View.OnClickListener() {

            @Override
            public void onClick(View paramView)
            {
                endDialog(DialogResult.CANCEL);
            }
            });
        findViewById(R.id.okBtn).setOnClickListener(new android.view.View.OnClickListener() {

            @Override
            public void onClick(View paramView)
            {
                endDialog(DialogResult.OK);
            }
            });
        }

    public void endDialog(int result)
    {
        dismiss();
        setDialogResult(result);
        Message m = mHandler.obtainMessage();
        mHandler.sendMessage(m);
    }

    public int showDialog()
    {
        mHandler = new Handler() {
            @Override
              public void handleMessage(Message mesg) {
                  // process incoming messages here
                //super.handleMessage(msg);
                throw new RuntimeException();
              }
          };
        super.show();
        try {
            Looper.getMainLooper().loop();
        }
        catch(RuntimeException e2)
        {
        }
        return dialogResult;
    }

}

回答by art926

Developers of Android and iOS decided that they are powerful and smart enough to reject Modal Dialog conception (that was on market for many-many years already and didn't bother anyone before), unfortunately for us. I believe that there is work around for Android - since you can show dialog from non-ui thread using Runnable class, there should be a way to wait in that thread (non-ui) until dialog is finished.

Android 和 iOS 的开发人员认为他们足够强大和聪明,可以拒绝模态对话框的概念(已经在市场上很多年了,之前没有打扰任何人),这对我们来说很不幸。我相信 Android 有解决方法 - 因为您可以使用 Runnable 类从非 ui 线程显示对话框,所以应该有一种方法可以在该线程(非 ui)中等待,直到对话框完成。

Edit: Here is my solution, it works great:

编辑:这是我的解决方案,效果很好:

    int pressedButtonID;
    private final Semaphore dialogSemaphore = new Semaphore(0, true);
    final Runnable mMyDialog = new Runnable()
    {
        public void run()
        {
            AlertDialog errorDialog = new AlertDialog.Builder( [your activity object here] ).create();
            errorDialog.setMessage("My dialog!");
            errorDialog.setButton("My Button1", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    pressedButtonID = MY_BUTTON_ID1;
                    dialogSemaphore.release();
                    }
                });
            errorDialog.setButton2("My Button2", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    pressedButtonID = MY_BUTTON_ID2;
                    dialogSemaphore.release();
                    }
                });
            errorDialog.setCancelable(false);
            errorDialog.show();
        }
    };

    public int ShowMyModalDialog()  //should be called from non-UI thread
    {
        pressedButtonID = MY_BUTTON_INVALID_ID;
        runOnUiThread(mMyDialog);
        try
        {
            dialogSemaphore.acquire();
        }
        catch (InterruptedException e)
        {
        }
        return pressedButtonID;
    }

回答by fupsduck

In an attempt to optimize memory and performance dialogs in Android are asynchronous (they are also managed for this reason). Comming from the Windows world, you are used to modal dialogs. Android dialogs are modal but more like non-modal when it comes to execution. Execution does not stop after displaying a dialog.

为了优化内存和性能,Android 中的对话框是异步的(出于这个原因,它们也被管理)。来自 Windows 世界,您已经习惯了模态对话框。Android 对话框是模态的,但在执行时更像是非模态的。显示对话框后不会停止执行。

The best description of Dialogs in Android I have seen is in "Pro Android" http://www.apress.com/book/view/1430215968

我见过的对 Android 中对话框的最佳描述是在“Pro Android”中http://www.apress.com/book/view/1430215968

This is not a perfect explanation but it should help you to wrap your brain around the differences between Dialogs in Windows and Android. In Windows you want to do A, ask a question with a dialog, and then do B or C. In android design A with all the code you need for B and C in the onClick() of the OnClickListener(s) for the dialog. Then do A and launch the dialog. You're done with A! When the user clicks a button B or C will get executed.

这不是一个完美的解释,但它应该可以帮助您了解 Windows 和 Android 中的对话框之间的差异。在 Windows 中你想做 A,用对话框问一个问题,然后做 B 或 C。 . 然后执行 A 并启动对话框。你已经完成了 A!当用户单击按钮 B 或 C 时将被执行。

Windows
-------
A code
launch dialog
user picks B or C
B or C code
done!

Android
-------
OnClick for B code (does not get executed yet)
OnClick for C code (does not get executed yet)
A code
launch dialog
done!
user picks B or C

回答by user545076

Ted, as you probably found out, you unfortunately can't do that on Android. Dialogs are modal, but asynchronous, and it will definitely disrupt the sequence you're trying to establish as you would have done on .NET (or Windows for that matter). You will have to twist your code around and break some logic that would have been very easy to follow based on your example.

Ted,正如您可能发现的那样,不幸的是,您无法在 Android 上做到这一点。对话框是模态的,但是是异步的,它肯定会破坏您尝试建立的序列,就像您在 .NET(或 Windows)上所做的那样。您将不得不扭曲您的代码并打破一些根据您的示例很容易遵循的逻辑。

Another very simple example is to save data in a file, only to find out that the file is already there and asking to overwrite it or not. Instead of displaying a dialog and having an if statement to act upon the result (Yes/No), you will have to use callbacks (called listeners in Java) and split your logic into several functions.

另一个非常简单的例子是将数据保存在文件中,只是发现文件已经存在并询问是否覆盖它。您将不得不使用回调(在 Java 中称为侦听器)并将您的逻辑拆分为多个函数,而不是显示对话框并使用 if 语句对结果进行操作。

On Windows, when a dialog is shown, the message pump continues in the background (only the current message being processed is on hold), and that works just fine. That allows users to move your app and let is repaint while you're displaying a dialog box for example. WinMo supports synchronous modal dialogs, so does BlackBerry but just not Android.

在 Windows 上,当显示一个对话框时,消息泵在后台继续运行(只有当前正在处理的消息处于保留状态),这工作得很好。例如,这允许用户移动您的应用程序并在您显示对话框时重新绘制。WinMo 支持同步模式对话框,BlackBerry 支持,但 Android 不支持。

回答by JohnnyBeGood

The cleanest and simplest solution is to use your own listener interface so that when the user clicks on the ok button your listener is called with the return value. This method does nothing fancy or complicated and respects android principles.

最干净和最简单的解决方案是使用您自己的侦听器界面,以便当用户单击确定按钮时,您的侦听器将被调用并返回值。这种方法没有什么花哨或复杂的东西,并且尊重android原则。

Define your listener interface as follows:

如下定义你的监听器接口:

public interface EditListener
/* Used to get an integer return value from a dialog
 * 
 */
{
 void returnValue(int value); 
}

For my application I created an EditValue class which uses AlertDialog and which I call whenever I want to edit an integer value. Note how the EditListener interface is passed as an argument to this code. When the user clicks on the OK button the value will be returned to your calling code via the EditListener method:

对于我的应用程序,我创建了一个 EditValue 类,它使用 AlertDialog,每当我想编辑一个整数值时我都会调用它。请注意 EditListener 接口如何作为参数传递给此代码。当用户单击 OK 按钮时,该值将通过 EditListener 方法返回给您的调用代码:

public final class EditValue
/* Used to edit a value using an alert dialog
 * The edited value is returned via the returnValue method of the supplied EditListener             interface
 * Could be modified with different constructors to edit double/float etc
 */
{
 public EditValue(final Activity parent, int value, String title, String message,
                  final EditListener editListener)
 {AlertDialog.Builder alert= new AlertDialog.Builder(parent);
  if(title==null) title= message;
  else if(message==null) message= title;
  if(title!=null) alert.setTitle(title);
  if(message!=null) alert.setMessage(message);

  // Set an EditText view to get user input 
  final EditText input = new EditText(parent);
  input.setText(String.valueOf(value));
  input.setInputType(InputType.TYPE_CLASS_NUMBER);
  alert.setView(input);

  alert.setPositiveButton("OK",new DialogInterface.OnClickListener()
  {public void onClick(DialogInterface dialog, int which)
   {try
    {int newValue= Integer.valueOf(input.getText().toString());
     editListener.returnValue(newValue);
     dialog.dismiss();
    }catch(NumberFormatException err) { }
   }
  });

  alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
  {public void onClick(DialogInterface dialog, int which)
   {dialog.dismiss();
    }
  });

  alert.show();
 }
}

Finally when you use EditValue you need to declare your EditListener and you can now access the return value and do what you want to do:

最后,当您使用 EditValue 时,您需要声明您的 EditListener,您现在可以访问返回值并执行您想做的操作:

 new EditValue(main,AnchorManager.anchorageLimit,
               main.res.getString(R.string.config_anchorage_limit),null,
               new EditListener()
 {public void returnValue(int value) {AnchorManager.anchorageLimit= value;}
  }
 );