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

来自.NET环境我现在正在研究如何在Android中使用对话框。

在.NET中,调用MessageBox.Show(…)时会创build并显示一个popup对话框。 在显示的调用中,我可以指定在popup窗口中应该可用的button,例如:

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

正如你所看到的,Show中的调用返回一个DialogResult,当在popup窗口中按下button时,通知我单击了哪个button。 请注意,在.NET中,在Show(…)调用的行被停止执行,所以当按下button时它可以返回值。

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

 myDialogResult == DialogResult.No 

因为我发现使用/创buildpopup窗口的.NET方法非常简单直观,所以我希望能够在Android中创buildpopup窗口。

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

问候


编辑1:要更清楚一点:

我需要暂停执行,并等待用户select了一个button来点击popup窗口。 显示对话框的调用之后的代码取决于在对话框中单击的button。

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

让我举个例子:

 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 

如果我想要执行依赖于popup窗口中的select,我将不得不使所有的variables在“正常执行”(在这种情况下nextStep )在onClick方法中可用,这听起来像对地狱。

编辑2

另一个显而易见的例子是popup窗口,询问“是否要继续”选项“是”“否”

如果用户按下“是”,则整个方法应该被中止,否则应该继续执行。 你如何解决这个很好?

问候

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

埃里克的答案正是你所需要的。 我知道这不是你想要的 ,但那并不重要。 我们devise了Android来防止开发人员编写同步对话框,所以你没有太多的select。

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

会给你一个与两个button的对话框,你用callback处理button点击。 您可能可以编写一些代码来使语法更类似于.NET,但是对话生命周期与“ Activity相互交织,所以最终可能比它的价值更麻烦。 其他对话框引用在这里 。

上面的丹尼尔答案的简化版本。 这个函数在警告对话框中得到一个是或否的用户,但可以很容易地修改以获得其他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; } 

在Android中,对话框是asynchronous的,所以你将不得不以不同的方式构造你的代码。

所以在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 } 

对于Android,它将不得不整齐。 像这样想。 你会有这样的OnClickListener

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

然后通过以下方法支持:

 void doOptionYes() { //do stuff for yes endThings(); } void doOptionNo() { //do stuff for no endThings(); } void endThings() { //clean up here } 

那么现在四种方法之一是什么? 它可能看起来不那么整洁,但恐怕是这样。

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

为了优化Android中的内存和性能对话框是asynchronous的(它们也是由于这个原因而被pipe理的)。 从Windows世界来的,你已经习惯了modal dialog。 Android对话框是模态的,但在执行时更像是非modal dialog。 显示对话框后,执行不会停止。

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

这不是一个完美的解释,但它应该帮助你把你的大脑包围在Windows和Android中的对话框之间的差异。 在Windows中,你想要做A,用对话框问一个问题,然后做B或C.在androiddeviseA中,为对话框的OnClickListener的onClick()提供B和C所需的所有代码。 然后执行A并启动对话框。 你已经完成了! 当用户点击一个buttonB或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 

特德,因为你可能发现,你不幸在Android上不能这样做。 对话框是模态的,但是是asynchronous的,它肯定会破坏你想要build立的序列,就像你在.NET(或Windows)上做的那样。 你将不得不扭曲你的代码,并打破一些本来很容易遵循的逻辑。

另一个非常简单的例子是将数据保存在一个文件中,只是发现该文件已经在那里,并要求覆盖或不覆盖。 而不是显示一个对话框,并有一个if语句作用于结果(是/否),您将不得不使用callback(在Java中称为侦听器)并将您的逻辑分成几个函数。

在Windows上,当显示一个对话框时,消息泵继续在后台(只有正在处理的当前消息处于保持状态),并且工作得很好。 这允许用户移动你的应用程序,让我们重画,例如,当你显示一个对话框。 WinMo支持同步模式对话框,黑莓手机,但不是Android。

Android和iOS的开发人员决定,他们非常强大而且足够聪明,可以拒绝Modal Dialog的概念(这已经在市场上已经有很多年了,之前没有任何麻烦)。 我相信有一些Android的工作 – 因为你可以使用Runnable类来显示非ui线程的对话框,所以应该有一种方法在该线程(非ui)中等待直到完成对话。

编辑:这是我的解决scheme,它很好用:

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

最干净和最简单的解决scheme是使用你自己的监听器接口,这样当用户点击okbutton时,你的监听器被返回值调用。 这个方法没有任何花哨或复杂,并尊重Android的原则。

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

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

对于我的应用程序,我创build了一个EditValue类,它使用AlertDialog,并且每当我想编辑一个整数值的时候我都会调用它。 请注意EditListener接口如何作为parameter passing给此代码。 当用户点击OKbutton时,该值将通过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(); } } 

最后,当你使用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;} } ); 

使用android.app.Dialog.setOnDismissListener(OnDismissListener监听器) 。

你设置onclick听众给你的button。 消除对话并做你的行动。 不需要停止任何事情

 protected Dialog onCreateDialog(int id) { return new AlertDialog.Builder(this).setTitle(R.string.no_connection).setIcon(android.R.drawable.ic_dialog_alert).setView(textEntryView).setPositiveButton(R.string.exit, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Here goes what you want to do } }) } 

调用 – ex – showDialog(DIALOG_ERROR_PREF);

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

只是为了回答你的问题…顺便说一句,我已经晚了9个月:D …有一个“解决方法”4这种问题。 即

 new AlertDialog.Builder(some_class.this).setTitle("bla").setMessage("bla bla").show(); wait(); 

只需添加wait ();

并且他们在OnClickListener中再次使用notify ()类来启动类

 @Override public void onClick(DialogInterface dialog, int item) { Toast.makeText(getApplicationContext(), "test", Toast.LENGTH_LONG).show(); **notify**(); dialog.cancel(); } 

相同的解决方法是在Android中使用4个敬酒和其他asynchronous调用

我是新来的Android / Java世界,很惊讶地发现在这里(除非我不明白我读的)模式对话框不工作。 对于我目前的一些非常模糊的原因,我得到了这个“ShowMessage”相当于一个确定的button,在我的平板电脑上以一种非常模式的方式工作。

从我的TDialogs.java模块:

 class DialogMes { AlertDialog alertDialog ; private final Message NO_HANDLER = null; public DialogMes(Activity parent,String aTitle, String mes) { alertDialog = new AlertDialog.Builder(parent).create(); alertDialog.setTitle(aTitle); alertDialog.setMessage(mes) ; alertDialog.setButton("OK",NO_HANDLER) ; alertDialog.show() ; } } 

以下是testing代码的一部分:

 public class TestDialogsActivity extends Activity implements DlgConfirmEvent { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button btShowMessage = (Button) findViewById(R.id.btShowMessage); btShowMessage.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { DialogMes dlgMes = new DialogMes( TestDialogsActivity.this,"Message","life is good") ; } }); 

遵循上面JohnnyBeGood提出的接口方法,我也实现了一个模式化的Yes / No对话框,它的工作原理也很好。

更正:

我的回答与我误解的问题无关。 出于某种原因,我把罗曼·盖伊(M. Romain Guy)解释为“你不想那么做”,作为一种莫不如此的对话。 我应该读到:“你不想这样做……”。

我道歉。

尝试在一个线程(而不是UI线程):

 final CountDownLatch latch = new CountDownLatch(1); handler.post(new Runnable() { @Override public void run() { OnClickListener okListener = new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); latch.countDown(); } }; AlertDialog dialog = new AlertDialog.Builder(context).setTitle(title) .setMessage(msg).setPositiveButton("OK", okListener).create(); dialog.show(); } }); try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } 
 UserSelect =null AlertDialog.Builder builder = new Builder(ImonaAndroidApp.LoginScreen); builder.setMessage("you message"); builder.setPositiveButton("OK", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { UserSelect = true ; } }); builder.setNegativeButton("Cancel", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { UserSelect = false ; } }); // in UI thread builder.show(); // wait until the user select while(UserSelect ==null); 

我正在使用Xamarin.Android(MonoDroid),并且我有开发UI Blocking确认框的要求。 我不打算和客户争论,因为我可以看到他们为什么要这样做的很好的理由( 细节在这里 ),所以我需要实现这一点。 我试过了@Daniel和@MindSpiker,但是这些在MonoForAndroid上并没有工作,在线程之间发送消息的时候,应用程序崩溃了。 我认为这是Xamarin映射。

我结束了从UI线程创build一个单独的线程,然后阻止,并等待用户响应如下:

 // (since the controllers code is shared cross-platforms) protected void RunConfirmAction(Action runnableAction) { if (runnableAction != null) { if (Core.Platform.IsAndroid) { var confirmThread = new Thread(() => runnableAction()); confirmThread.Start(); } else { runnableAction(); } } } // The call to the logout method has now changed like this: RunConfirmAction(Logout); // the implemtation of the MessageBox waiting is like this: public DialogResult MessageBoxShow(string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton) { if (_CurrentContext != null && _CurrentContext.Screen != null && MainForm.MainActivity != null) { Action<bool> callback = OnConfirmCallBack; _IsCurrentlyInConfirmProcess = true; Action messageBoxDelegate = () => MessageBox.Show(((Activity)MainForm.MainActivity), callback, message, caption, buttons); RunOnMainUiThread(messageBoxDelegate); while (_IsCurrentlyInConfirmProcess) { Thread.Sleep(1000); } } else { LogHandler.LogError("Trying to display a Message box with no activity in the CurrentContext. Message was: " + message); } return _ConfirmBoxResult ? DialogResult.OK : DialogResult.No; } private void OnConfirmCallBack(bool confirmResult) { _ConfirmBoxResult = confirmResult; _IsCurrentlyInConfirmProcess = false; } private bool _ConfirmBoxResult = false; private bool _IsCurrentlyInConfirmProcess = false; 

关于如何做到这一点的详细信息可以在我的博客文章中find

重写:

移动和桌面环境之间以及应用程序开发到几年前和今天的方式之间存在着根本性的差异:

a)移动设备需要节约能源。 他们提供的价值的一部分。 所以你需要节省资源。 线程是一个昂贵的资源。 暂停一个线程的进度是这个资源的一个不可接受的浪费。

b)现在用户要求更高。 为了给他们提供帮助,我们认为它应该拥有完整的CPU和尽可能小的能源消耗。 它的应用程序不是一个人在设备上,有一个未知的数量的另一个应用程序在同一时间运行,你的应用程序不一定是最紧急的。

c)系统级锁不是一种select:移动设备在后台使用一些事件和服务,并且不适合任何应用程序locking它们。

考虑一下用户在“系统locking”正在工作时接到电话。

基于上述事实,提出的问题的答案是:

  • 有一个可行的方法来build立一个阻塞主线程的对话框,直到用户的响应?

不会。用户体验变差的方法会变得更糟,并且可能会导致指责系统本身的错误。 这是不公平的,惩罚平台和所有的开发者。

  • 有没有办法通过对话来阻止整个系统?

不可以。这个在平台上是严格禁止的。 没有应用程序可以干扰系统或其他应用程序的操作。

  • 我需要重构我的应用程序,或者重新思考我的编程方式,以适应Android移动系统体系结构。

是。 包括这方面。

这是最简单的方法:

 new AlertDialog.Builder(this).setTitle("title").setMessage("message").create().show();