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


исходящий от .Чистой окружающей среды им теперь хотите понять, как работать диалоговые окна в Android.

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

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

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

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

myDialogResult == DialogResult.No

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

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

в отношении


EDIT 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:

другим очевидным примером может быть всплывающее окно с запросом "хочешь продолжить" с параметрами "да" и "нет".

если пользователь нажимает "да", весь метод должен быть прерван, в противном случае следует продолжить выполнение. Как вы решаете это красиво?

в отношении

18 65

18 ответов:

Ted, вы не хотите этого делать, на самом деле :) самая большая причина заключается в том, что если вы блокируете поток пользовательского интерфейса во время отображения диалога, вы заблокируете поток, который отвечает за рисование и обработку событий вашего диалога. Это означает, что ваш диалог не будет отвечать на запросы. Вы также вызовете ANRs, если пользователю потребуется более нескольких секунд, чтобы щелкнуть диалоговое окно.

Эрих-это именно то, что вам нужно. Я знаю, что это не то, что вы хочу, но это не важно. Мы разработали 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 являются модальными, но больше похожи на немодальные, когда дело доходит до выполнения. Выполнение не останавливается после отображения диалогового окна.

лучшее описание диалогов в Android я видел в "Pro Android" http://www.apress.com/book/view/1430215968

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

изменить: Вот мое решение, оно работает отлично:

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

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

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

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

самое чистое и простое решение-использовать свой собственный интерфейс прослушивателя, чтобы, когда пользователь нажимает на кнопку 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;}
  }
 );

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

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

a) мобильные устройства должны экономить энергию. Часть стоимости, которую они предлагают. Так что вам нужно экономить ресурсы. Потоки-это дорогостоящий ресурс. Остановка прогресса потока является недопустимой тратой этого ресурса.

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

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

подумайте о том, что пользователь получает телефонный звонок, пока работает ваша "системная блокировка"...

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

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

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

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

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

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

да. В том числе и в этом аспекте.

вы устанавливаете прослушиватели onclick для вас кнопки. dissmis диалог и сделать свое действие. Не нужно ничего останавливать

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

просто ответить на ваш вопрос...кстати sry, что я на 9 месяцев опоздал: D...есть "обходной путь" 4 такого рода проблем. то есть

new AlertDialog.Builder(some_class.this).setTitle("bla").setMessage("bla bla").show();
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.Ява модуль:

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

устранение:

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

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

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

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

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

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