Диалоги / AlertDialogs: как «блокировать выполнение» во время диалога вверх (стиль .NET)

Исходя из .NET-среды Теперь я хочу понять, как работают Dialogs в Android.

В .NET при вызове MessageBox.Show (…), который создает и показывает всплывающее диалоговое окно. В вызове Show я могу указать, какие кнопки должны быть доступны во всплывающем окне, например:

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

Как вы можете видеть, вызов Show возвращает DialogResult при нажатии кнопки во всплывающем окне, информируя меня о том, какая кнопка была нажата. Обратите внимание, что в .NET выполнение приостанавливается в строке, где выполняется вызов Show (…) , поэтому он может вернуть значение при нажатии кнопки.

Если я в приведенном выше примере нажать «Нет», myDialogResult будет равен

 myDialogResult == DialogResult.No 

Поскольку я нахожу .NET-способ использования / создания всплывающих окон очень простым и интуитивно понятным, мне бы хотелось, чтобы этот способ создания всплывающих окон в Android тоже.

Итак, вопрос в том, кто-нибудь знает, как «остановить выполнение», как с помощью MessageBox.Show, а затем вернуть значение всякий раз, когда нажимается кнопка (и диалог уходит)?

С уважением


РЕДАКТИРОВАТЬ 1: Чтобы быть немного более ясным:

Мне нужно, чтобы выполнение остановилось и подождало, пока пользователь не выберет кнопку, чтобы щелкнуть всплывающее окно. Код, который следует за вызовом для отображения диалогового окна, зависит от того, какая кнопка нажата в диалоговом окне.

Вот почему я не могу использовать то, что предлагают Эрих и Алекс, поскольку писать код в методах 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 

Если бы я хотел, чтобы выполнение зависело от выбора во всплывающем окне, мне бы как-то пришлось сделать все переменные в «обычном исполнении» (в данном случае nextStep ), доступном в методах onClick , и это звучит как ад для меня ,

EDIT 2 :

Другим очевидным примером будет всплывающее окно с запросом «Вы хотите продолжить» с параметрами «Да» и «Нет» .

Если пользователь нажимает «Да», весь метод следует прервать, иначе он должен продолжить выполнение. Как вы это хорошо решаете?

С уважением

Тед, вы не хотите этого делать, действительно 🙂 Самая большая причина в том, что если вы блокируете поток пользовательского интерфейса, пока вы показываете диалог, вы блокируете поток, который отвечает за рисование и обработку событий вашего диалога. Это означает, что ваш диалог будет невосприимчив. Вы также вызовите ANR, если пользователь заберет более нескольких секунд, чтобы щелкнуть диалог.

Ответ Эриха – именно то, что вам нужно. Я знаю, что это не то, что вы хотите , но это не имеет значения. Мы разработали Android, чтобы разработчики не писали синхронные диалоги, поэтому у вас действительно нет большого выбора.

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

Вы получите диалог с двумя кнопками, и вы будете обрабатывать нажатия кнопок с обратными вызовами. Возможно, вы сможете написать код, чтобы синтаксис больше напоминал .NET, но жизненный цикл диалога довольно переплетается с Activity , поэтому, в конце концов, это может быть больше проблем, чем того стоит. Дополнительные ссылки на диалоговые окна приведены здесь .

Упрощенная версия ответа Даниила выше. Эта функция получает да или нет от пользователя в диалоговом окне предупреждения, но может быть легко изменена для получения другого ввода.

 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 диалоги являются асинхронными, поэтому вам придется структурировать код немного по-другому.

Итак, в 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 являются асинхронными (они также управляются по этой причине). Поступая из мира Windows, вы привыкли к модальным диалогам. Диалоги Android являются модальными, но больше похожи на немодальные, когда дело доходит до исполнения. Выполнение не прекращается после отображения диалогового окна.

Лучшее описание Dialogs в Android, которое я видел, находится в «Pro Android» http://www.apress.com/book/view/1430215968

Это не идеальное объяснение, но оно должно помочь вам обернуть мозг вокруг различий между диалоговыми окнами в Windows и Android. В Windows вы хотите сделать A, задать вопрос с помощью диалога, а затем сделать B или C. В андроидном дизайне A со всем кодом, необходимым для B и C в onClick () для OnClickListener (s) для диалога , Затем сделайте 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 

Тед, как вы, наверное, узнали, вы, к сожалению, не можете сделать это на Android. Диалоги являются модальными, но асинхронными, и это определенно нарушит последовательность, которую вы пытаетесь установить, как вы бы сделали на .NET (или Windows, если на то пошло). Вам придется крутить свой код вокруг и нарушить некоторую логику, которую было бы очень легко следовать на основе вашего примера.

Еще один очень простой пример – сохранить данные в файле, только чтобы узнать, что файл уже существует и просит перезаписать его или нет. Вместо того, чтобы отображать диалоговое окно и иметь инструкцию if, чтобы воздействовать на результат (Да / Нет), вам придется использовать обратные вызовы (называемые слушателями в Java) и разделять вашу логику на несколько функций.

В Windows, когда отображается диалоговое окно, насос сообщений продолжает работать в фоновом режиме (только текущее обрабатываемое сообщение находится в режиме ожидания), и это работает нормально. Это позволяет пользователям перемещать ваше приложение и позволяет перерисовывать, когда вы показываете диалоговое окно, например. WinMo поддерживает синхронные модальные диалоги, так же как и BlackBerry, но не Android.

Разработчики Android и iOS решили, что они достаточно мощные и умные, чтобы отклонить концепцию Modal Dialog (которая была на рынке уже много-много лет и никого не беспокоила), к сожалению для нас. Я считаю, что для Android есть работа – поскольку вы можете показывать диалог из потока non-ui с использованием класса Runnable, должен быть способ подождать в этом потоке (не-ui), пока диалог не будет завершен.

Редактировать: Вот мое решение, оно отлично работает:

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

Самое простое и простое решение – использовать свой собственный интерфейс слушателя, чтобы при нажатии пользователем кнопки ok ваш слушатель вызывается с возвращаемым значением. Этот метод не делает ничего необычного или сложного и уважает принципы android.

Определите интерфейс слушателя следующим образом:

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

Для моего приложения я создал класс 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(); } } 

Наконец, когда вы используете 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 слушателей кнопками. Диалоги dismis и сделайте свое действие. Не нужно ничего останавливать

 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

Просто чтобы ответить на ваш вопрос … btw sry, что я на 9 месяцев опоздал: D … есть «обходной путь» 4 таких проблем. т.е.

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

Просто добавьте wait ();

И они в OnClickListener снова начинают класс с уведомлением () что-то вроде этого

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

То же обходное решение – 4 тоста и другие асинхронные вызовы в android

Я новичок в мире Android / Java и был удивлен, узнав здесь (если я не понимаю, что я читал), что модальные диалоги не работают. Для некоторых очень неясных причин для меня на данный момент я получил этот эквивалент «ShowMessage» с кнопкой «ok», которая работает на моем планшете очень модально.

Из моего модуля 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() ; } } 

Вот часть тестового кода:

 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, и он тоже неплохо работает.

Исправление:

Мой ответ не имеет отношения к вопросу, который я неправильно понял. По какой-то причине я интерпретировал М. Ромена Гая «ты не хочешь этого делать», как «нет» для модальных диалогов. Я должен был прочитать: «Вы не хотите делать это … таким образом».

Я извиняюсь.

Попробуйте это в потоке (а не в пользовательском интерфейсе):

 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), и у меня есть требования для разработки окна подтверждения блокировки пользовательского интерфейса. Я не собираюсь спорить с клиентом, потому что я могу видеть веские причины, почему они этого хотят ( подробности здесь ), поэтому мне нужно это реализовать. Я попробовал @Daniel и @MindSpiker выше, но они не работали над MonoForAndroid, когда сообщение отправлено между потоками, приложение разбилось. Я предполагаю, что это связано с отображением Xamarin.

Я закончил создание отдельного потока из потока пользовательского интерфейса, а затем заблокировал его и ожидал ответа пользователя следующим образом:

 // (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; 

Полную информацию о том, как это сделать, можно найти в моем сообщении в блоге здесь

Переписывание:

Существуют радикальные различия между мобильной и настольной средой, а также между тем, как приложения разрабатывались до нескольких лет назад и сегодня:

А) мобильным устройствам необходимо экономить энергию. Часть стоимости, которую они предлагают. Поэтому вам нужно сэкономить ресурсы. Нитки – дорогой ресурс. Остановить ход потока – это неприемлемая трата этого ресурса.

Б) В настоящее время пользователь гораздо более требователен. Чтобы помочь им, мы считаем, что он заслуживает того, чтобы иметь полный рабочий процессор и минимально возможное расходование энергии. Его приложение не является единственным устройством, в то же время неизвестно количество других приложений, работающих в одно и то же время, и ваше приложение не обязательно является самым срочным.

C) блокировки на системном уровне не являются опцией: мобильное устройство работает с несколькими событиями и службами в фоновом режиме, и это неправильно для любого из них, которое может быть заблокировано приложением.

Подумайте о том, что пользователь получает телефонный звонок, когда работает «системная блокировка» …

Исходя из приведенных выше фактов, ответ на предлагаемый вопрос:

  • Существует жизнеспособный способ построения диалога, который блокирует основной поток до ответа пользователя?

Нет. Обходные методы ухудшают работу пользователя, и это может привести к ошибке привинчивания самой системы. Это несправедливо и наказывает платформу и всех ее разработчиков.

  • Есть ли способ заблокировать всю систему с помощью диалога?

Нет. Это строго запрещено на платформе. Ни одно приложение не может вмешиваться в работу системы или других приложений.

  • Мне нужно реорганизовать мое приложение или переосмыслить свой способ программирования в соответствии с моей архитектурой мобильной системы Android.

Да. Включая этот аспект.

Это самый простой способ:

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