Диалоги/AlertDialogs: как "блокировать выполнение" при диалоге вверх (стиль .NET)
Исходя из .NET-среды, я сейчас пытаюсь понять, как работают диалоги в 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
) доступными в методах nextStep
, и это звучит для меня как ад,
Редактировать 2
Другим очевидным примером может быть всплывающее окно с вопросом "Хотите продолжить" с опциями "Да" и "Нет".
Если пользователь нажимает "Да", весь метод должен быть прерван, в противном случае он должен продолжить выполнение. Как вы решаете это красиво?
Ответы
Ответ 1
Тед, вы не хотите этого делать, действительно:) Самая большая причина в том, что если вы блокируете поток пользовательского интерфейса, пока вы показываете диалог, вы блокируете поток, который отвечает за рисование и обработку событий ваш диалог. Это означает, что ваш диалог будет неактуальным. Вы также будете вызывать ANR, если пользователю потребуется больше нескольких секунд, чтобы щелкнуть диалог.
Ответ Erich - это именно то, что вам нужно. Я знаю, что это не то, что вы хотите, но это не имеет значения. Мы разработали Android, чтобы разработчики не писали синхронные диалоги, поэтому у вас действительно нет выбора.
Ответ 2
В 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
, поэтому в конце концов, это может быть больше проблем, чем того стоит. Дополнительные ссылки на диалог здесь.
Ответ 3
Упрощенная версия Даниэля выше. Эта функция получает да или нет от пользователя в диалоговом окне предупреждения, но может быть легко изменена для получения другого ввода.
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;
}
Ответ 4
В диалоговом окне Android диалоги асинхронны, поэтому вам придется структурировать код немного по-другому.
Итак, в С# ваша логика запускала что-то вроде этого в псевдокоде:
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
}
Итак, какой был один метод, теперь четыре. Это может показаться не таким аккуратным, но, как это работает, я боюсь.
Ответ 5
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;
}
}
Ответ 6
В попытке оптимизировать диалоги памяти и производительности в 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
Ответ 7
Разработчики 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;
}
Ответ 8
Тед, как вы, наверное, узнали, вы, к сожалению, не можете сделать это на Android. Диалоги являются модальными, но асинхронными, и это определенно нарушит последовательность, которую вы пытаетесь установить, как вы бы сделали на .NET(или Windows, если на то пошло). Вам придется крутить свой код и нарушить некоторую логику, которую было бы очень легко следовать на основе вашего примера.
Еще один очень простой пример - сохранить данные в файле, только чтобы узнать, что файл уже существует и просит перезаписать его или нет. Вместо того, чтобы отображать диалог и иметь инструкцию if, чтобы воздействовать на результат (Да/Нет), вам придется использовать обратные вызовы (называемые слушателями на Java) и разделить вашу логику на несколько функций.
В Windows, когда отображается диалоговое окно, насос сообщений продолжает работать в фоновом режиме (только текущее сообщение обрабатывается в режиме ожидания), и это работает очень хорошо. Это позволяет пользователям перемещать ваше приложение и позволяет перерисовывать, когда вы показываете диалоговое окно, например. WinMo поддерживает синхронные модальные диалоги, так же как и BlackBerry, но не Android.
Ответ 9
Самое простое и простое решение - использовать свой собственный интерфейс прослушивателя, чтобы при нажатии пользователем кнопки ok ваш слушатель вызывается с возвращаемым значением. Этот метод не делает ничего необычного или сложного и уважает принципы android.
Определите интерфейс слушателя следующим образом:
public interface EditListener
/* Used to get an integer return value from a dialog
*
*/
{
void returnValue(int value);
}
Для моего приложения я создал класс EditValue, который использует AlertDialog и который я вызываю всякий раз, когда я хочу изменить целочисленное значение. Обратите внимание, как интерфейс EditListener передается в качестве аргумента для этого кода. Когда пользователь нажимает кнопку ОК, значение будет возвращено вашему вызывающему коду с помощью метода 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;}
}
);
Ответ 10
Используйте android.app.Dialog.setOnDismissListener(прослушиватель OnDismissListener).
Ответ 11
Переписывание:
Существуют радикальные различия между мобильной и рабочей средой, а также между тем, как приложения разрабатывались до нескольких лет назад и сегодня:
a) мобильным устройствам необходимо экономить энергию. Часть стоимости, которую они предлагают. Поэтому вам нужно экономить ресурсы. Темы - дорогой ресурс. Остановить ход потока - это неприемлемая трата этого ресурса.
b) В настоящее время пользователь намного более требователен. Чтобы помочь им, мы считаем, что он заслуживает того, чтобы иметь полный рабочий процессор и минимально возможное расходование энергии. Его приложение не единственное на устройстве, в то же время неизвестно количество других приложений, работающих в одно и то же время, и ваше приложение не обязательно является самым срочным.
c) блокировки на системном уровне не являются опцией: мобильное устройство работает с несколькими событиями и службами в фоновом режиме, и это неправильно для любого из них, которое может быть заблокировано приложением.
Подумайте о том, что пользователь получает телефонный звонок, когда работает "системная блокировка"...
Основываясь на приведенных выше фактах, ответ на предлагаемый вопрос:
- Существует жизнеспособный способ создания диалога, который блокирует основной поток до ответа пользователя?
Нет. Обходные решения ухудшают работу пользователей, и это может привести к ошибке привинчивания самой системы. Это несправедливо и наказывает платформу и всех ее разработчиков.
- Есть ли способ заблокировать всю систему с помощью диалога?
Нет. Это строго запрещено на платформе. Ни одно приложение не может вмешиваться в работу системы или других приложений.
- Мне нужно реорганизовать мое приложение или переосмыслить свой способ программирования в соответствии с моей архитектурой мобильной системы Android.
Да. Включая этот аспект.
Ответ 12
вы устанавливаете 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
Ответ 13
просто чтобы ответить на ваш вопрос... btw sry, что я опоздал на 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();
}
то же обходное решение отправляет 4 тоста и другие асинхронные вызовы в android
Ответ 14
Попробуйте это в потоке (а не в пользовательском интерфейсе):
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();
}
Ответ 15
Я новичок в мире 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, и он тоже неплохо работает.
Исправление:
Мой ответ не имеет отношения к вопросу, который я неправильно понял. По какой-то причине я интерпретировал М. Ромена Гая "вы не хотите этого делать", как "нет" для модальных диалогов. Я должен был прочитать: "Вы не хотите делать это... таким образом".
Извиняюсь.
Ответ 16
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);
Ответ 17
Я использую 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;
Подробная информация о том, как это сделать, можно найти в моем сообщении в блоге здесь
Ответ 18
Это самый простой способ:
new AlertDialog.Builder(this).setTitle("title").setMessage("message").create().show();