Подтвердить что ты не робот

Android: Как получить модальный диалог или подобное модальное поведение?

В эти дни я работаю над имитацией модального диалога в Android. Я много гугл, много дискуссий, но, к сожалению, там не так много вариантов, чтобы получить это модальное. Вот какой фон,
Диалоги, модальные диалоги и блокировка
Диалоги /AlertDialogs: Как выполнить "кадровое выполнение" а диалог вверх (стиль .NET)

Нет никакого прямого способа получить модальное поведение, тогда я придумал 3 возможных решения,
1. Используйте диалоговое действие, например, этот поток, но я все еще не могу заставить основной вид активности ожидать ожидание возврата диалогового окна. Основная деятельность повернулась к статусу остановки и снова была перезапущена.
2. Создайте один рабочий поток и используйте синхронизацию потоков. Тем не менее, это огромная работа по рефакторингу для моего приложения, теперь у меня есть одно основное действие и сервис как в основном потоке пользовательского интерфейса.
3. Возьмите обработку событий в цикле, когда есть модальный диалог, и завершите цикл, когда диалог закрывается. На самом деле это способ создания реального модального диалога, как то, что он делает в Windows. Я все еще не прототипировал этот путь.

Я все равно хотел бы имитировать его с помощью диалоговой работы,
1. Запустить диалог-активность с помощью startActivityForResult()
2. получить результат от onActivityResult()
Здесь некоторый источник

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

Вызывающий startAlertDialog блокирует выполнение и ожидает возвращенный результат. Но startAlertDialog возвратился сразу, конечно, и основное действие перешло в состояние STOP, в то время как DialogActivity встал.

Итак, вопрос в том, как заставить основной вид активности ждать результата?
Спасибо.

4b9b3361

Ответ 1

Я получил модальный диалог при использовании:

setCancelable(false);

в диалоговом окне диалога (не на DialogBuilder).

Ответ 2

Невозможно, как вы планировали. Во-первых, вам запрещено блокировать поток пользовательского интерфейса. Ваше приложение будет прекращено. Во-вторых, необходимо обработать методы жизненного цикла, которые вызывается при запуске другого действия с помощью startActivity (ваша первоначальная активность будет приостановлена, пока выполняется другое действие). В-третьих, вы, возможно, каким-то образом взломали его, используя startAlertDialog() не из потока пользовательского интерфейса, с синхронизацией потоков (например, Object.wait()) и некоторой AlertDialog. Однако я сильно рекомендую вам не делать этого. Это уродливо, безусловно, сломается, и это просто не то, как все должно работать.

Перепроектируйте свой подход, чтобы зафиксировать асинхронный характер этих событий. Если вы хотите, например, какое-то диалоговое окно, которое запрашивает у пользователя решение (например, принятие ToS или нет) и выполняет специальные действия на основе этого решения, создайте диалоговое окно, подобное этому:

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

Затем у вас есть два метода обработки положительной или отрицательной обратной связи соответственно (т.е. продолжение какой-либо операции или завершение операции или что-то имеющее смысл).

Ответ 3

Разработчики Android и iOS решили, что они достаточно мощные и умные, чтобы отвергнуть концепцию Modal Dialog (которая была на рынке уже много-много лет и никого не беспокоила), к сожалению, для нас.

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

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

Ответ 4

Это работает для меня: создайте Activity как ваш диалог. Тогда,

  • Добавьте это в свой манифест для активности:

    Android: тема = "@андроид: стиль /Theme.Dialog"

  • Добавьте это в onCreate своей деятельности

    setFinishOnTouchOutside (false);

  • Переопределить onBackPressed в вашей деятельности:

    @Override public void onBackPressed() { // препятствовать "возврату" от оставления этой активности }

Первый показывает активность в диалоге. Последние два ведут себя как модальный диалог.

Ответ 5

Наконец, я закончил с действительно простым и простым решением.

Люди, знакомые с программированием Win32, могут знать, как реализовать модальный диалог. Как правило, он запускает вложенный цикл сообщений (через GetMessage/PostMessage), когда есть модальный диалог вверх. Итак, я попытался реализовать свой собственный модальный диалог таким традиционным способом.

Во-первых, андроид не предоставлял интерфейсы для ввода в цикл сообщений нити ui, или я его не нашел. Когда я посмотрел в источник, Looper.loop(), я нашел именно то, что хотел. Но все же MessageQueue/Message не предоставил публичные интерфейсы. К счастью, у нас есть отражение в java. В основном, я просто скопировал то, что сделал Looper.loop(), он заблокировал рабочий процесс и все еще правильно обработал события. Я не тестировал вложенный модальный диалог, но теоретически это сработало бы.

Здесь мой исходный код

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

Надеюсь, это поможет.

Ответ 6

Это не сложно.

Предположим, что у вас есть флаг активности вашего владельца (с именем waiting_for_result), когда ваша активность возобновляется:

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

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

Ответ 7

Одно из решений:

  • Поместите весь код для каждой выбранной кнопки в слушатель каждой кнопки.
  • alert.show(); должна быть последней строкой кода в функции, вызывающей предупреждение. Любой код после этой строки не будет ждать, чтобы закрыть оповещение, но будет выполняться немедленно.

Надеемся на помощь!

Ответ 8

Как отмечают hackbod и другие, Android намеренно не предоставляет метод для выполнения вложенных циклов событий. Я понимаю причины этого, но есть определенные ситуации, которые их требуют. В нашем случае у нас есть собственная виртуальная машина, работающая на разных платформах, и мы хотели ее перенести на Android. Внутри там много мест, где требуется вложенный цикл событий, и на самом деле не реально переписывать все это только для Android. В любом случае, вот решение (в основном взятое из Как я могу выполнять обработку неблокирующих событий на Android?, но я добавил тайм-аут):

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

Ответ 9

У меня есть аналогичное решение, например fifth, но его немного проще и не нуждается в отражении. Я думал, почему используйте исключение для выхода из петлителя. Итак, мой пользовательский петлитель читается следующим образом:

1) Исключение:

final class KillException extends RuntimeException {
}

2) Пользовательский петлитель:

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

}

Использование настраиваемого петлителя довольно просто. предполагать у вас есть диалог foo, а затем просто выполните следующее где вы хотите вызвать диалог foo modally:

a) При вызове в foo:

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

Внутри диалога foo, когда вы хотите выйти, вы просто вызовите метод quit для настраиваемого петлителя. Это выглядит следующим образом:

b) При выходе из foo:

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

Недавно я видел некоторые проблемы с 5.1.1 Android, не вызывайте модальный диалог из главного меню, вместо событие, которое вызывает модальный диалог. Без публикации главное меню будет стоять, и я видел Looper:: pollInner() SIGSEGVs в моем приложении.

Ответ 10

Я не уверен, что это 100% модальный, так как вы можете нажать на какой-либо другой компонент, чтобы закрыть диалоговое окно, но я запутался с конструкциями циклов, и поэтому предлагаю это как еще одну возможность. Он работал хорошо для меня, поэтому я хотел бы поделиться этой идеей. Вы можете создать и открыть диалоговое окно одним способом, а затем закрыть его в методе обратного вызова, и программа будет ожидать ответа на диалог перед выполнением метода обратного вызова. Если вы затем запустите оставшуюся часть метода обратного вызова в новом потоке, диалоговое окно также закроется первым, прежде чем остальная часть кода будет выполнена. Единственное, что вам нужно сделать, это иметь глобальную переменную диалогового окна, чтобы различные методы могли ее получить. Так может работать что-то вроде следующего:

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