Диалоги/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;}
  }
 );

Ответ 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();