Dialogs / AlertDialogs: Cómo "bloquear la ejecución" mientras el diálogo está hacia arriba (estilo .NET)

Procedente del entorno .NET Ahora estoy buscando entender cómo funcionan los cuadros de diálogo en Android.

En .NET, al llamar a MessageBox.Show (…) que crea y muestra un cuadro de diálogo emergente. En la llamada a Mostrar, puedo especificar qué botones deben estar disponibles en la ventana emergente, por ejemplo:

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

Como se puede ver, la llamada a Show devuelve un DialogResult cuando se presiona un botón en la ventana emergente, informándome de qué botón se ha hecho clic. Tenga en cuenta que en .NET, la ejecución se detiene en la línea donde se realiza la llamada a Show (…) , por lo que puede devolver el valor cuando se presiona un botón.

Si en el ejemplo anterior presiono "No" el myDialogResult será igual a

 myDialogResult == DialogResult.No 

Puesto que encuentro la forma .NET de usar / crear pop-ups muy fácil e intuitiva, me gustaría esa manera de crear pop-ups en Android también.

Por lo tanto, la pregunta es si alguien sabe "detener la ejecución" como con el MessageBox.Show, y luego devolver un valor cada vez que se pulsa el botón (y el diálogo se va)?

Saludos


EDIT 1: Para ser un poco más claro:

Necesito para la ejecución para detener y esperar hasta que el usuario ha elegido un botón para hacer clic en el popup. El código que sigue a la llamada para mostrar el cuadro de diálogo depende de qué botón se hace clic en el cuadro de diálogo.

Es por eso que no puedo usar lo que sugieren Erich y Alex, ya que escribir código en los métodos onClick como se sugiere a continuación no funcionará. La razón es que no puedo continuar la "ejecución normal". Permítanme tomar un ejemplo:

Permítanme tomar un ejemplo:

 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 

Si yo quisiera que la ejecución fuera dependiente de la elección en el popup, tendría que hacer de alguna manera todas las variables en la "ejecución normal" (en este caso nextStep ) disponible en los métodos onClick, y eso suena como un infierno para mí .

EDIT 2 :

Otro ejemplo obvio sería un popup preguntando "¿Quieres continuar" con las opciones "Sí" y "No" .

Si el usuario pulsa "Sí", todo el método debe ser abortado de lo contrario debe continuar la ejecución. ¿Cómo lo solucionas bien?

Saludos

Ted, usted no quiere hacer esto, realmente 🙂 La razón más grande es que si bloquea el hilo de interfaz de usuario mientras está mostrando un diálogo, bloqueará el hilo que está a cargo de dibujar y manejar los eventos de su diálogo. Lo que significa que su diálogo no responderá. También causará ANR si el usuario tarda más de unos segundos en hacer clic en el diálogo.

La respuesta de Erich es exactamente lo que usted necesita. Sé que no es lo que quieres , pero eso no importa. Hemos diseñado Android para evitar que los desarrolladores escriban diálogos síncronos para que no tengas mucha opción.

En Android, la estructura es diferente de .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(); 

Recibirá un diálogo con dos botones y controlará los clics de botón con devoluciones de llamada. Es posible que pueda escribir algún código para hacer que la sintaxis se parezca más a. NET, pero el ciclo de vida del diálogo está bastante entrelazado con Activity , así que al final, podría ser más problemático de lo que vale la pena. Aquí hay referencias de diálogo adicionales.

Una versión simplificada de la respuesta de Daniel arriba. Esta función obtiene un sí o un no del usuario en un cuadro de diálogo de alerta, pero podría modificarse fácilmente para obtener otra entrada.

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

En Android los cuadros de diálogo son asíncronos, así que tendrás que estructurar tu código un poco diferente.

Así que en C # tu lógica funcionaba algo así en pseudocódigo:

 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 } 

Para Android, va a tener que ser menos ordenado. Piense en ello como así. Tendrás un OnClickListener como este:

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

Que es soportado por los siguientes métodos:

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

Así que lo que era un método es ahora cuatro. Puede que no parezca tan ordenado, pero eso es lo que funciona, me temo.

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

En un intento de optimizar la memoria y los diálogos de rendimiento en Android son asíncronos (también se administran por este motivo). Desde el mundo de Windows, usted está acostumbrado a los diálogos modales. Los cuadros de diálogo de Android son modales pero más no modales cuando se trata de ejecución. La ejecución no se detiene después de mostrar un diálogo.

La mejor descripción de los cuadros de diálogo en Android que he visto es en "Pro Android" http://www.apress.com/book/view/1430215968

Esto no es una explicación perfecta, pero debería ayudarle a envolver su cerebro sobre las diferencias entre los cuadros de diálogo en Windows y Android. En Windows desea hacer A, hacer una pregunta con un cuadro de diálogo y luego hacer B o C. En el diseño androide A con todo el código que necesita para B y C en el onClick () de OnClickListener (s) para el diálogo . A continuación, haga A y ejecute el diálogo. ¡Ya terminaste con A! Cuando el usuario hace clic en un botón B o C se ejecutará.

 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 

Ted, como probablemente se enteró, por desgracia no se puede hacer eso en Android. Los cuadros de diálogo son modales, pero asíncronos, y definitivamente interrumpirán la secuencia que estás tratando de establecer como lo habrías hecho en .NET (o Windows para esa materia). Usted tendrá que girar su código alrededor y romper una lógica que habría sido muy fácil de seguir sobre la base de su ejemplo.

Otro ejemplo muy simple es guardar datos en un archivo, sólo para descubrir que el archivo ya está allí y pidiendo sobrescribirlo o no. En lugar de mostrar un diálogo y tener una instrucción if para actuar sobre el resultado (Sí / No), tendrá que usar devoluciones de llamada (llamados oyentes en Java) y dividir su lógica en varias funciones.

En Windows, cuando se muestra un cuadro de diálogo, la bomba de mensajes continúa en segundo plano (sólo el mensaje actual que se está procesando está en espera) y funciona bien. Eso permite a los usuarios mover su aplicación y dejar que se repinta mientras está mostrando un cuadro de diálogo, por ejemplo. WinMo soporta diálogos síncronos modales, lo mismo ocurre con BlackBerry, pero no con Android.

Los desarrolladores de Android e iOS decidieron que son poderosos y lo suficientemente inteligentes como para rechazar la concepción de Modal Dialog (que estaba en el mercado durante muchos años y ya no molestó a nadie antes), desafortunadamente para nosotros. Creo que hay trabajo alrededor para Android – puesto que usted puede demostrar el diálogo del hilo no-ui usando la clase Runnable, allí debe ser una manera de esperar en ese hilo de rosca (no-ui) hasta que se termine el diálogo.

Edit: Aquí está mi solución, que funciona muy bien:

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

La solución más limpia y más simple es usar su propia interfaz de escucha para que cuando el usuario haga clic en el botón Aceptar su oyente se llama con el valor de retorno. Este método no hace nada extravagante o complicado y respeta los principios de Android.

Defina su interfaz de escucha de la siguiente manera:

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

Para mi aplicación creé una clase EditValue que utiliza AlertDialog y que llamo cuando quiero editar un valor entero. Observe cómo se transmite la interfaz EditListener como un argumento a este código. Cuando el usuario hace clic en el botón Aceptar, el valor se devolverá a su código de llamada mediante el método 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(); } } 

Finalmente cuando usas EditValue debes declarar tu EditListener y ahora puedes acceder al valor devuelto y hacer lo que quieres hacer:

  new EditValue(main,AnchorManager.anchorageLimit, main.res.getString(R.string.config_anchorage_limit),null, new EditListener() {public void returnValue(int value) {AnchorManager.anchorageLimit= value;} } ); 

Use android.app.Dialog.setOnDismissListener (OnDismissListener escucha) .

Usted fija los botones del onclick a usted. Dismis diálogo y hacer su acción. No es necesario detener nada

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

Para llamar – ex – showDialog (DIALOG_ERROR_PREF);

Más http://developer.android.com/guide/topics/ui/dialogs.html

Sólo para responder a su pregunta … por favor sry que estoy 9 meses de retraso: D … hay una "solución" 4 este tipo de problemas. es decir

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

Simplemente agregue wait ();

Y ellos en OnClickListener iniciar la clase de nuevo con notify () algo como esto

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

La misma solución pasa 4 brindis y otras llamadas asíncronas en android

Soy nuevo en el mundo de Android / Java y me sorprendió descubrir aquí (a menos que no entiendo lo que leí) que los diálogos modales no funcionan. Por algunas razones muy oscuras para mí en este momento, tengo este "ShowMessage" equivalente con un botón de ok que funciona en mi tableta de una manera muy modal.

Desde mi módulo 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() ; } } 

Aquí es parte del código de prueba:

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

También implementé un cuadro de diálogo modal Sí / No siguiendo el enfoque de interfaz sugerido anteriormente por JohnnyBeGood, y funciona bastante bien también.

Corrección:

Mi respuesta no es relevante para la pregunta que mal entendí. Por alguna razón, interpreté a M. Romain Guy "no quieres hacer eso" como un no no a los diálogos modales. Debería haber leído: "no quieres hacer eso … de esta manera".

Me disculpo.

Pruebe esto en un hilo (no en el subproceso):

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

Estoy utilizando Xamarin.Android (MonoDroid), y tengo requirments para el desarrollo de bloqueo de la interfaz de usuario cuadro de confirmación. No voy a discutir con el cliente porque puedo ver buenas razones de por qué quieren que ( detalles aquí ), así que tengo que implementar esto. Intenté @Daniel y @MindSpiker arriba, pero éstos no trabajaron en MonoForAndroid, el momento que el mensaje se envía entre los hilos, la aplicación se estrelló. Supongo que es algo que ver con la cartografía Xamarin.

Terminé creando un hilo separado del hilo de interfaz de usuario y luego bloquear eso y esperar la respuesta del usuario de la siguiente manera:

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

Los detalles completos sobre cómo hacer esto se pueden encontrar en mi entrada del blog aquí

Reescritura:

Hay diferencias radicales entre el entorno móvil y de escritorio, así como entre la forma en que las aplicaciones se desarrollaron a un puñado de años y hoy:

A) los dispositivos móviles necesitan ahorrar energía. Parte del valor que ofrecen. Así que necesitas ahorrar recursos. Los hilos son un recurso costoso. Detener el progreso de un hilo es un desperdicio inaceptable de este recurso.

B) Hoy en día el usuario es mucho más exigente. Para ayudarles, creemos que merece tener CPU de funcionamiento completo y el menor gasto posible de energía. Su aplicación no está sola en el dispositivo, hay un número desconocido de otras aplicaciones que se ejecutan al mismo tiempo y su aplicación no es necesariamente la más urgente.

C) los bloqueos a nivel de sistema no son una opción: un dispositivo móvil funciona con una serie de eventos y servicios en segundo plano y no es correcto para cualquiera de ellos puede ser bloqueado por una aplicación.

Piense en el usuario que recibe una llamada telefónica mientras su "bloqueo del sistema" está funcionando …

Con base en los hechos anteriores, la respuesta a la pregunta propuesta es:

  • Hay una manera viable de construir un diálogo que bloquea el hilo principal hasta una respuesta del usuario?

No. Soluciones alternativas empeoran la experiencia del usuario y puede cometer el error de culpar al propio sistema. Esto es injusto y penaliza la plataforma y todos sus desarrolladores.

  • ¿Hay una forma de bloquear todo el sistema con un diálogo?

No. Esto está estrictamente prohibido en la plataforma. Ninguna aplicación puede interferir con el funcionamiento del sistema u otras aplicaciones.

  • Necesito refactorizar mi aplicación, o repensar mi forma de programación para adaptarme a la arquitectura del sistema móvil Android.

Sí. Incluyendo este aspecto.

Esta es la forma más sencilla:

 new AlertDialog.Builder(this).setTitle("title").setMessage("message").create().show(); 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.