Android: How to get a modal dialog or similar modal behavior?

These days I'm working on simulating a modal dialog in Android. I have a lot of Google, a lot of discussion, but, unfortunately, there are not many options to get this modal. Here is the background
Dialogs, modal dialogs and locking
Dialogs / AlertDialogs: How to perform "personnel execution" and a dialog up (.NET style)

There is no direct way to get modal behavior, then I came up with 3 possible solutions,
1. Use a dialog action, for example, this thread , but I still cannot get the main activity to wait for the dialog to return. The main activity turned to stop status and was restarted again.
2. Create one workflow and use thread synchronization. However, this is a huge refactoring job for my application, now I have one main action and service as in the main user interface thread.
3. Take event handling in a loop when there is a modal dialog, and end the loop when the dialog closes. This is actually a way of creating a real modal dialogue, like what it does on Windows. I still haven't prototyped this way.

Anyway, I would like to imitate it using dialogue work,
1. Launch the activity dialog using startActivityForResult ()
2. get the result from onActivityResult ()
Here is some source

public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MyView v = new MyView(this); setContentView(v); } private final int RESULT_CODE_ALERT = 1; private boolean mAlertResult = false; public boolean startAlertDialog() { Intent it = new Intent(this, DialogActivity.class); it.putExtra("AlertInfo", "This is an alert"); startActivityForResult(it, RESULT_CODE_ALERT); // I want to wait right here return mAlertResult; } @Override protected void onActivityResult (int requestCode, int resultCode, Intent data) { switch (requestCode) { case RESULT_CODE_ALERT: Bundle ret = data.getExtras(); mAlertResult = ret.getBoolean("AlertResult"); break; } } } 

The calling startAlertDialog blocks execution and waits for the returned result. But startAlertDialog returned right away, of course, and the main action went into STOP, while DialogActivity got up.

So, the question is how to make the main type of activity wait for the result?
Thank.

+46
android synchronization android-activity modal-dialog
May 25 '11 at 6:47 am
source share
10 answers

I got a modal dialog when using:

 setCancelable(false); 

in a dialog box (not on DialogBuilder).

+56
Feb 25 '13 at 7:29
source share

Impossible as you planned. First, you are not allowed to block the user interface thread. Your application will be terminated. Secondly, you need to handle the lifecycle methods that are called when another action is launched using startActivity (your initial activity will be suspended while another action is being performed). Thirdly, you may have somehow cracked it using startAlertDialog() not from a UI thread, with thread synchronization (e.g. Object.wait() ) and some AlertDialog . However, I strongly recommend that you do not. This ugly will certainly break, and it's just not how things should work.

Redesign your approach to capture the asynchronous nature of these events. If you want, for example, a dialog box that asks the user for a solution (for example, accepting ToS or not) and performs special actions based on this decision, create a dialog box similar to this:

 AlertDialog dialog = new AlertDialog.Builder(context).setMessage(R.string.someText) .setPositiveButton(android.R.string.ok, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); // Do stuff if user accepts } }).setNegativeButton(android.R.string.cancel, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); // Do stuff when user neglects. } }).setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { dialog.dismiss(); // Do stuff when cancelled } }).create(); dialog.show(); 

Then you have two methods for processing positive or negative feedback, respectively (i.e., continuing an operation or completing an operation or something that makes sense).

+12
May 25 '11 at 8:58 a.m.
source share

Android and iOS developers decided that they are powerful enough and smart enough to reject the concept of Modal Dialog (which has been on the market for many, many years and did not bother anyone), unfortunately, for us.

Here is my solution, it works great:

  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; } 
+6
Jul 14 '12 at 1:45
source share

This works for me: create an Activity as your dialog. Then,

  • Add this to your manifest for activity:

    Android: theme = "@ android: style /Theme.Dialog"

  • Add this to your activity onCreate

    setFinishOnTouchOutside (false);

  • Override onBackPressed in your activity:

    @Override public void onBackPressed () {// prevent the "return" from leaving this activity}

The first shows the activity in the dialogue. The last two behave like a modal dialogue.

+4
Feb 06 '13 at 16:22
source share

Finally, I ended up with a really simple and easy solution.

People familiar with Win32 programming may know how to implement a modal dialog. Typically, it starts a nested message loop (via GetMessage / PostMessage) when there is a modal dialog up. So, I tried to implement my own modal dialogue in this traditional way.

Firstly, the android did not provide interfaces for entering the ui thread into the message loop, or I did not find it. When I looked at the source, Looper.loop (), I found exactly what I wanted. But still, MessageQueue / Message did not provide public interfaces. Fortunately, we have a reflection in java. Basically, I just copied what Looper.loop () did, it blocked the workflow and still handled the events correctly. I did not test the nested modal dialog, but theoretically this would work.

Here is my source code

 public class ModalDialog { private boolean mChoice = false; private boolean mQuitModal = false; private Method mMsgQueueNextMethod = null; private Field mMsgTargetFiled = null; public ModalDialog() { } public void showAlertDialog(Context context, String info) { if (!prepareModal()) { return; } // build alert dialog AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setMessage(info); builder.setCancelable(false); builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { ModalDialog.this.mQuitModal = true; dialog.dismiss(); } }); AlertDialog alert = builder.create(); alert.show(); // run in modal mode doModal(); } public boolean showConfirmDialog(Context context, String info) { if (!prepareModal()) { return false; } // reset choice mChoice = false; AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setMessage(info); builder.setCancelable(false); builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { ModalDialog.this.mQuitModal = true; ModalDialog.this.mChoice = true; dialog.dismiss(); } }); builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { ModalDialog.this.mQuitModal = true; ModalDialog.this.mChoice = false; dialog.cancel(); } }); AlertDialog alert = builder.create(); alert.show(); doModal(); return mChoice; } private boolean prepareModal() { Class<?> clsMsgQueue = null; Class<?> clsMessage = null; try { clsMsgQueue = Class.forName("android.os.MessageQueue"); } catch (ClassNotFoundException e) { e.printStackTrace(); return false; } try { clsMessage = Class.forName("android.os.Message"); } catch (ClassNotFoundException e) { e.printStackTrace(); return false; } try { mMsgQueueNextMethod = clsMsgQueue.getDeclaredMethod("next", new Class[]{}); } catch (SecurityException e) { e.printStackTrace(); return false; } catch (NoSuchMethodException e) { e.printStackTrace(); return false; } mMsgQueueNextMethod.setAccessible(true); try { mMsgTargetFiled = clsMessage.getDeclaredField("target"); } catch (SecurityException e) { e.printStackTrace(); return false; } catch (NoSuchFieldException e) { e.printStackTrace(); return false; } mMsgTargetFiled.setAccessible(true); return true; } private void doModal() { mQuitModal = false; // get message queue associated with main UI thread MessageQueue queue = Looper.myQueue(); while (!mQuitModal) { // call queue.next(), might block Message msg = null; try { msg = (Message)mMsgQueueNextMethod.invoke(queue, new Object[]{}); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } if (null != msg) { Handler target = null; try { target = (Handler)mMsgTargetFiled.get(msg); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } if (target == null) { // No target is a magic identifier for the quit message. mQuitModal = true; } target.dispatchMessage(msg); msg.recycle(); } } } } 

Hope this helps.

+3
Jun 01 '11 at 8:19
source share

It's not hard.

Suppose you have your owner’s activity flag (named waiting_for_result ) when your activity resumes:

 public void onResume(){ if (waiting_for_result) { // Start the dialog Activity } } 

This guaranteed the owner’s activity, if the modal dialogue is not rejected, whenever he tries to get focus, he goes into modal dialogue mode.

+1
May 25 '11 at 6:55
source share

One solution:

  • Put the entire code for each selected button in the listener of each button.
  • alert.show(); must be the last line of code in the function causing the warning. Any code after this line will not wait to close the alert, but will be executed immediately.

Hope to help!

+1
Aug 24 2018-12-12T00:
source share

As hackbod and others note, Android does not intentionally provide a method for executing nested event loops. I understand the reasons for this, but there are certain situations that require them. In our case, we have our own virtual machine running on different platforms, and we wanted to transfer it to Android. Inside there are many places where a nested event loop is required, and it’s not really possible to rewrite all this only for Android. Anyway, here is the solution (mostly taken from How can I handle non-blocking events on Android? But I added a timeout):

 private class IdleHandler implements MessageQueue.IdleHandler { private Looper _looper; private int _timeout; protected IdleHandler(Looper looper, int timeout) { _looper = looper; _timeout = timeout; } public boolean queueIdle() { _uiEventsHandler = new Handler(_looper); if (_timeout > 0) { _uiEventsHandler.postDelayed(_uiEventsTask, _timeout); } else { _uiEventsHandler.post(_uiEventsTask); } return(false); } }; private boolean _processingEventsf = false; private Handler _uiEventsHandler = null; private Runnable _uiEventsTask = new Runnable() { public void run() { Looper looper = Looper.myLooper(); looper.quit(); _uiEventsHandler.removeCallbacks(this); _uiEventsHandler = null; } }; public void processEvents(int timeout) { if (!_processingEventsf) { Looper looper = Looper.myLooper(); looper.myQueue().addIdleHandler(new IdleHandler(looper, timeout)); _processingEventsf = true; try { looper.loop(); } catch (RuntimeException re) { // We get an exception when we try to quit the loop. } _processingEventsf = false; } } 
+1
Sep 12 '13 at 16:20
source share

I have a similar solution, for example fifth , but it is a bit simpler and does not need to be reflected. I thought why use an exception to exit the looper. So my custom looper reads like this:

1) Exception:

 final class KillException extends RuntimeException { } 

2) Custom looper:

 public final class KillLooper implements Runnable { private final static KillLooper DEFAULT = new KillLooper(); private KillLooper() { } public static void loop() { try { Looper.loop(); } catch (KillException x) { /* */ } } public static void quit(View v) { v.post(KillLooper.DEFAULT); } public void run() { throw new KillException(); } } 

Using a custom looper is pretty simple. Assume you have a foo dialog, and then just do the following where you want to call the foo modally dialog:

a) When called in foo:

 foo.show(); KillLooper.loop(); 

Inside the foo dialog, when you want to exit, you simply call the quit method on the custom looper. It looks like this:

b) When exiting foo:

 dismiss(); KillLooper.quit(getContentView()); 

Recently I saw some problems with 5.1.1 Android, do not call the modal dialog from the main menu, instead of the event that causes the modal dialog. Without publishing, the main menu will stand, and I saw Looper :: pollInner () SIGSEGVs in my application.

+1
May 31 '15 at 9:18
source share

I'm not sure if this is 100% modal, since you can click on some other component to close the dialog box, but I got confused with the constructions of the loops, and therefore I propose this as another possibility. It worked well for me, so I would like to share this idea. You can create and open a dialog box in one way, and then close it in the callback method, and the program will wait for a response to the dialog before executing the callback method. If you then run the rest of the callback method in a new thread, the dialog will also close first before the rest of the code is executed. The only thing you need to do is to have a global dialog variable so that the various methods can get it. So something like the following might work:

 public class MyActivity extends ... { /** Global dialog reference */ private AlertDialog okDialog; /** Show the dialog box */ public void showDialog(View view) { // prepare the alert box AlertDialog.Builder alertBox = new AlertDialog.Builder(...); ... // set a negative/no button and create a listener alertBox.setNegativeButton("No", new DialogInterface.OnClickListener() { // do something when the button is clicked public void onClick(DialogInterface arg0, int arg1) { //no reply or do nothing; } }); // set a positive/yes button and create a listener alertBox.setPositiveButton("Yes", new DialogInterface.OnClickListener() { // do something when the button is clicked public void onClick(DialogInterface arg0, int arg1) { callbackMethod(params); } }); //show the dialog okDialog = alertBox.create(); okDialog.show(); } /** The yes reply method */ private void callbackMethod(params) { //first statement closes the dialog box okDialog.dismiss(); //the other statements run in a new thread new Thread() { public void run() { try { //statements or even a runOnUiThread } catch (Exception ex) { ... } } }.start(); } } 
+1
Jan 27 '16 at 12:32
source share



All Articles