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

后端 未结 11 1920
暗喜
暗喜 2020-12-01 05:17

These days I\'m working on simulating modal dialog in Android. I\'ve googled a lot, there\'s much discussions but sadly there\'s not much options to get it modal. Here\'s so

11条回答
  •  孤城傲影
    2020-12-01 05:31

    Finally I ended up with a really straight and simple solution.

    People who's familiar with Win32 programming possibly knows how to implement a modal dialog. Generally it runs a nested message loop (by GetMessage/PostMessage) when there is a modal dialog up. So, I tried to implement my own modal dialog in this traditional way.

    At the first, android didn't provide interfaces to inject into ui thread message loop, or I didn't find one. When I looked into source, Looper.loop(), I found it's exactly what I wanted. But still, MessageQueue/Message haven't provided public interfaces. Fortunately, we have reflection in java. Basically, I just copied exactly what Looper.loop() did, it blocked workflow and still properly handled events. I haven't tested nested modal dialog, but theoretically it would work.

    Here's 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();
            }
        }
    }
    }
    

    Hopefully this would help.

提交回复
热议问题