Dialogs / AlertDialogs: Comment «bloquer l’exécution» pendant que la boîte de dialog est active (style .NET)

Venant de l’environnement .NET, je cherche maintenant à comprendre comment fonctionnent les dialogs dans Android.

Dans .NET, lorsque vous appelez MessageBox.Show (…) qui crée et affiche une boîte de dialog contextuelle. Dans l’appel à Show, je peux spécifier quels boutons devraient être disponibles dans la fenêtre contextuelle, par exemple:

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

Comme vous pouvez le voir, l’appel à Show renvoie un DialogResult lorsqu’un bouton est pressé dans la fenêtre, m’informant du bouton sur lequel vous avez cliqué. Notez que dans .NET, l’exécution est arrêtée à la ligne où l’appel à Show (…) est effectué, afin de pouvoir renvoyer la valeur lorsqu’un bouton est pressé.

Si dans l’exemple ci-dessus, appuyez sur “Non”, myDialogResult sera égal à

 myDialogResult == DialogResult.No 

Etant donné que la manière d’utiliser / créer des fenêtres contextuelles avec .NET est très simple et intuitive, j’aimerais que cette manière de créer des fenêtres contextuelles soit également disponible dans Android.

Donc, la question est de savoir si quelqu’un sait “arrêter l’exécution” comme avec MessageBox.Show, puis renvoyer une valeur chaque fois que le bouton est pressé (et la boîte de dialog disparaît)?

Cordialement


EDIT 1: Pour être un peu plus clair:

J’ai besoin que l’exécution s’arrête et que l’utilisateur ait choisi un bouton pour cliquer dans la fenêtre contextuelle. Le code qui suit l’appel pour afficher la boîte de dialog dépend du bouton sur lequel vous cliquez.

C’est pourquoi je ne peux pas utiliser ce que Erich et Alex suggèrent, car écrire du code dans les méthodes onClick comme suggéré ci-dessous ne fonctionnera pas. La raison en est que je ne peux pas continuer “l’exécution normale”. Laissez-moi prendre un exemple:

Laissez-moi prendre un exemple:

 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 je voulais que l’exécution dépende du choix dans la fenêtre contextuelle, je devrais en quelque sorte rendre toutes les variables de “l’exécution normale” (dans ce cas, nextStep ) disponibles dans les méthodes onClick, et cela me semble dommage. .

EDIT 2 :

Un autre exemple évident serait une fenêtre contextuelle demandant “Voulez-vous continuer” avec les options “Oui” et “Non” .

Si l’utilisateur appuie sur “Oui”, la méthode entière doit être annulée sinon elle doit continuer à être exécutée. Comment résolvez-vous cela gentiment?

Cordialement

Ted, vous ne voulez pas faire ça, vraiment 🙂 La principale raison est que si vous bloquez le thread d’interface utilisateur pendant que vous affichez un dialog, vous allez bloquer le thread chargé de dessiner et de gérer les événements de votre boîte de dialog. Ce qui signifie que votre dialog ne répondra plus. Vous provoquerez également des ANR si l’utilisateur prend plus de quelques secondes pour cliquer sur la boîte de dialog.

La réponse d’Erich est exactement ce dont vous avez besoin. Je sais que ce n’est pas ce que vous voulez , mais peu importe. Nous avons conçu Android pour empêcher les développeurs d’écrire des dialogs synchrones afin que vous n’ayez pas vraiment le choix.

Dans Android, la structure est différente 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(); 

Vous obtenez un dialog avec deux boutons et vous manipulez les clics de bouton avec des rappels. Vous pourriez être en mesure d’écrire du code pour que la syntaxe ressemble plus à .NET, mais le cycle de vie du dialog est étroitement lié à l’ Activity . Des références de dialog supplémentaires sont ici .

Une version simplifiée de la réponse de Daniel ci-dessus Cette fonction obtient un oui ou un non de l’utilisateur dans une boîte de dialog d’alerte mais peut facilement être modifiée pour obtenir d’autres entrées.

 private boolean mResult; public boolean getYesNoWithExecutionStop(Ssortingng title, Ssortingng 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 sortingggered. try { Looper.loop(); } catch(RuntimeException e2) {} return mResult; } 

Dans Android Dialogs sont asynchrones, vous allez donc devoir structurer votre code un peu différemment.

Donc, en C #, votre logique fonctionnait comme ceci dans un pseudocode:

 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 } 

Pour Android, il va falloir être moins net. Pensez-y comme ça. Vous aurez un OnClickListener comme ceci:

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

Ce qui est alors pris en charge par les méthodes suivantes:

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

Donc, une méthode est maintenant quatre. Cela peut ne pas sembler aussi soigné mais c’est comme ça que ça marche, j’ai peur.

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

Pour tenter d’optimiser la mémoire et les performances, les dialogs sous Android sont asynchrones (ils sont également gérés pour cette raison). Depuis le monde Windows, vous êtes habitué aux boîtes de dialog modales. Les dialogs Android sont modaux mais ressemblent davantage à des modes non modaux en matière d’exécution. L’exécution ne s’arrête pas après l’affichage d’une boîte de dialog.

La meilleure description de Dialogs dans Android que j’ai vue est dans “Pro Android” http://www.apress.com/book/view/1430215968

Ce n’est pas une explication parfaite, mais cela devrait vous aider à comprendre les différences entre les dialogs dans Windows et Android. Dans Windows, vous voulez faire A, posez une question avec une boîte de dialog, puis faites B ou C. Dans la conception Android, utilisez tout le code nécessaire pour B et C dans le onClick () du ou des OnClickListener pour la boîte de dialog. . Ensuite, faites A et lancez la boîte de dialog. Vous avez terminé avec A! Lorsque l’utilisateur clique sur un bouton, B ou C sera exécuté.

 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 

Les développeurs d’Android et d’iOS ont décidé qu’ils étaient suffisamment puissants et intelligents pour rejeter la conception de Modal Dialog (qui était déjà sur le marché depuis de nombreuses années et qui ne dérangeait personne auparavant), malheureusement pour nous. Je crois qu’il y a du travail autour d’Android – puisque vous pouvez afficher un dialog à partir d’un thread non-ui en utilisant la classe Runnable, il devrait y avoir un moyen d’attendre dans ce thread (non-ui) jusqu’à ce que le dialog soit terminé.

Edit: Voici ma solution, ça marche super 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; } 

Ted, comme vous l’avez probablement découvert, vous ne pouvez malheureusement pas le faire sur Android. Les dialogs sont modaux, mais asynchrones, et cela va définitivement perturber la séquence que vous essayez d’établir comme vous l’auriez fait sur .NET (ou Windows en fait). Vous devrez tordre votre code et briser une logique qui aurait été très facile à suivre sur la base de votre exemple.

Un autre exemple très simple consiste à enregistrer des données dans un fichier, uniquement pour découvrir que le fichier est déjà présent et demander à le remplacer ou non. Au lieu d’afficher une boîte de dialog et d’avoir une instruction if pour agir sur le résultat (Yes / No), vous devrez utiliser des callbacks (appelés listeners en Java) et diviser votre logique en plusieurs fonctions.

Sous Windows, lorsqu’une boîte de dialog s’affiche, la pompe de messages continue en arrière-plan (seul le message en cours de traitement est en attente), et cela fonctionne correctement. Cela permet aux utilisateurs de déplacer votre application et de laisser repeindre pendant que vous affichez une boîte de dialog par exemple. WinMo prend en charge les boîtes de dialog modales synchrones, tout comme BlackBerry, mais pas Android.

La solution la plus simple et la plus simple consiste à utiliser votre propre interface d’écouteur afin que, lorsque l’utilisateur clique sur le bouton ok, votre écouteur soit appelé avec la valeur de retour. Cette méthode ne fait rien de compliqué ni de compliqué et respecte les principes androïdes.

Définissez votre interface d’écoute comme suit:

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

Pour mon application, j’ai créé une classe EditValue qui utilise AlertDialog et que j’appelle chaque fois que je veux modifier une valeur entière. Notez comment l’interface EditListener est transmise en tant qu’argument à ce code. Lorsque l’utilisateur clique sur le bouton OK, la valeur sera renvoyée à votre code d’appel via la méthode 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, Ssortingng title, Ssortingng 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(Ssortingng.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().toSsortingng()); 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(); } } 

Enfin, lorsque vous utilisez EditValue, vous devez déclarer votre EditListener et vous pouvez maintenant accéder à la valeur de retour et faire ce que vous voulez faire:

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

Utilisez android.app.Dialog.setOnDismissListener (écouteur OnDismissListener) .

Réécriture:

Il y a des différences radicales entre l’environnement mobile et l’environnement de bureau, ainsi qu’entre la manière dont les applications ont été développées il y a quelques années et aujourd’hui:

a) les appareils mobiles doivent économiser de l’énergie. Une partie de la valeur qu’ils offrent. Vous devez donc économiser des ressources. Les threads sont une ressource coûteuse. Arrêter la progression d’un fil est un gaspillage inacceptable de cette ressource.

b) De nos jours, l’utilisateur est beaucoup plus exigeant. Pour les aider, nous pensons qu’elle mérite d’avoir un processeur complet et une dépense d’énergie minimale. Son application n’est pas la seule sur l’appareil, il y a un nombre inconnu d’applications tournant simultanément et votre application n’est pas nécessairement la plus urgente.

c) les verrous au niveau du système ne sont pas une option: un appareil mobile fonctionne avec un certain nombre d’événements et de services en arrière-plan et ce n’est pas correct car l’un d’entre eux peut être verrouillé par une application.

Pensez à l’utilisateur qui reçoit un appel téléphonique pendant que votre “locking du système” fonctionne …

Sur la base des faits ci-dessus, la réponse à la question proposée est la suivante:

  • Il existe un moyen viable de créer un dialog qui bloque le thread principal jusqu’à une réponse de l’utilisateur?

Non. Les solutions de contournement sont-elles de plus en plus difficiles à utiliser pour l’utilisateur, et cela peut entraîner l’erreur de blâmer le système lui-même. Ceci est injuste et pénalise la plate-forme et tous ses développeurs.

  • Y a-t-il un moyen de bloquer tout le système avec un dialog?

Non, ceci est ssortingctement interdit sur la plateforme. Aucune application ne peut interférer avec le fonctionnement du système ou d’autres applications.

  • Je dois refactoriser mon application ou repenser ma façon de programmer en fonction de l’architecture du système mobile Android.

Oui. Y compris cet aspect.

vous définissez onclick listeners sur vos boutons. Diffuser le dialog et faire votre action. Pas besoin d’arrêter quoi que ce soit

 protected Dialog onCreateDialog(int id) { return new AlertDialog.Builder(this).setTitle(R.ssortingng.no_connection).setIcon(android.R.drawable.ic_dialog_alert).setView(textEntryView).setPositiveButton(R.ssortingng.exit, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Here goes what you want to do } }) } 

appeler – ex-showDialog (DIALOG_ERROR_PREF);

Plus http://developer.android.com/guide/topics/ui/dialogs.html

juste pour répondre à votre question … en tout cas, je suis en retard de 9 mois: D … il y a une “solution de rechange” 4 à ce genre de problèmes. c’est à dire

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

ajoutez simplement wait ();

et eux dans le OnClickListener démarrez la classe à nouveau avec notify () quelque chose comme ça

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

la même solution de contournement va 4 toasts et autres appels asynchrones dans Android

Je suis nouveau dans le monde Android / Java et j’ai été surpris de découvrir ici (à moins que je ne comprenne pas ce que je lis) que les dialogs modaux ne fonctionnent pas. Pour certaines raisons très obscures pour moi en ce moment, j’ai obtenu cet équivalent “ShowMessage” avec un bouton ok qui fonctionne sur ma tablette de manière très modale.

De mon module TDialogs.java:

 class DialogMes { AlertDialog alertDialog ; private final Message NO_HANDLER = null; public DialogMes(Activity parent,Ssortingng aTitle, Ssortingng mes) { alertDialog = new AlertDialog.Builder(parent).create(); alertDialog.setTitle(aTitle); alertDialog.setMessage(mes) ; alertDialog.setButton("OK",NO_HANDLER) ; alertDialog.show() ; } } 

Voici une partie du code de test:

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

J’ai également implémenté une boîte de dialog modale Oui / Non selon l’approche d’interface suggérée ci-dessus par JohnnyBeGood, et cela fonctionne très bien aussi.

Correction:

Ma réponse ne concerne pas la question que j’ai mal comprise. Pour une raison quelconque, j’ai interprété M. Romain Guy “tu ne veux pas faire ça” comme un dialog non-modal. J’aurais dû lire: “tu ne veux pas faire ça … de cette façon”.

Je m’excuse.

Essayez ceci dans un thread (pas le thread de l’interface utilisateur):

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

J’utilise Xamarin.Android (MonoDroid), et j’ai des exigences pour le développement de la boîte de confirmation du blocage de l’interface utilisateur. Je ne vais pas me disputer avec le client car je peux voir de bonnes raisons pour lesquelles il le souhaite ( détails ici ), alors je dois le mettre en œuvre. J’ai essayé @Daniel et @MindSpiker ci-dessus, mais ceux-ci ne fonctionnaient pas sur MonoForAndroid, au moment où le message est envoyé entre les threads, l’application est bloquée. Je suppose que cela a quelque chose à voir avec la cartographie Xamarin.

J’ai fini par créer un thread séparé à partir du thread d’interface utilisateur, puis j’ai bloqué cela et j’ai attendu la réponse de l’utilisateur comme suit:

 // (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(ssortingng message, ssortingng caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton) { if (_CurrentContext != null && _CurrentContext.Screen != null && MainForm.MainActivity != null) { Action 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; 

Tous les détails sur la façon de procéder peuvent être trouvés dans mon blog ici

C’est le moyen le plus simple:

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