Java: как прервать программу на полпути выполнения щелчком мыши

Как можно было бы реализовать mouselistener (или каким-то другим способом, не имеет значения), который будет обрабатывать событие щелчка мыши в ЛЮБОЙ части программы? Предпочтительно возвращаться к строке, на которой он остановился, когда метод обработчика события клика завершится.

Я использую свинг. «Контекст» — это графический интерфейс, который постоянно обновляется, но должен реагировать на щелчок мыши от пользователя в любое время без задержки. Действительно, у меня есть опыт работы с событиями, использования и перезаписи их обработчиков и т. д., не слишком глубокого, я полагаю, но того, что я знаю, было достаточно во всем до сих пор.


person ZamielTheGreat    schedule 28.01.2012    source источник
comment
Несколько вопросов: Какую библиотеку GUI вы используете, Swing? SWT? Каков здесь контекст - что конкретно вы пытаетесь сделать (не так, как вы описываете)? Знакомы ли вы с концепциями, лежащими в основе событийно-ориентированного программирования?   -  person Hovercraft Full Of Eels    schedule 28.01.2012
comment
Я почти уверен, что вы захотите использовать потоки. Один (или более) для графического интерфейса и один для прослушивателя событий щелчка мыши.   -  person Joe Phillips    schedule 28.01.2012


Ответы (2)


Я не мог понять ваш первый абзац, поэтому мой ответ касается вашего второго абзаца, если я правильно его понял. ;)

Swing следует модели с одним потоком. Таким образом, вы должны обновлять пользовательский интерфейс из потока рассылки событий (EDT). Этот поток также отвечает за доставку событий в ваш код, отсюда и название. Если вы постоянно обновляете пользовательский интерфейс в цикле, это приведет к тому, что EDT будет занят и заблокирован. Конечным эффектом будет пользовательский интерфейс, который не реагирует на пользовательские события. Это потому, что события ставятся в очередь, и EDT может выбрать их и доставить в ваш код, когда он станет бесплатным.

Игры обычно сталкиваются с таким сценарием. Вы могли заметить, что игры обычно имеют одну фиксированную частоту обновления, которую они называют FPS (кадров в секунду). Обычно достаточно поддерживать 60 кадров в секунду. То есть вам нужно отрисовывать свой пользовательский интерфейс 50 раз в секунду, но сейчас кажется, что ваш цикл рендеринга (который обновляет пользовательский интерфейс) работает непрерывно.

Вам нужно, чтобы отдельный поток постоянно работал, который отвечает за отрисовку пользовательского интерфейса. Это должно отрисовываться в буфер (Image). А затем вызовите repaint() для элемента пользовательского интерфейса, который нужно обновить. paintComponent() этого элемента пользовательского интерфейса необходимо переопределить, чтобы он мог копировать изображение в буфер Image и рисовать его в графическом контексте.

Теперь начинается настоящая хитрость. Цикл, который вызывает repaint(), должен выполнить некоторые арифметические действия, чтобы убедиться, что он не выходит за пределы отрисовки 60 раз, т. е. зацикливания 60 раз в секунду. Если и когда это произойдет, то он должен вызвать Thread.sleep(sleepTime), где sleepTime — количество миллисекунд, оставшихся в секунду после 60-кратного цикла. Может случиться так, что вашему циклу потребуется более секунды для завершения 60 итераций, тогда не просто переходите к следующей итерации, а вызываете Thread.yield(). Это даст другим потокам возможность использовать ЦП, например. может быть, ваш EDT. Чтобы усложнить ситуацию, не продолжайте уступать всегда, поэтому, возможно, стоит добавить некоторую логику, чтобы убедиться, что уступка выполняется только x последовательных раз. Этот последний сценарий должен быть очень редким, если вообще. Этот сценарий означает, что система находится под большой нагрузкой.

Помните, что repaint() является потокобезопасным и может вызываться из любого потока. Он планирует вызов paint() по восточноевропейскому времени. Таким образом, вызов repaint() не гарантирует краску. Таким образом, вы можете поэкспериментировать с различными значениями FPS, чтобы найти то, которое вам подходит.

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

Дальнейшее чтение:-

person AppleGrew    schedule 28.01.2012
comment
Я понимаю, что вы имеете в виду, я прошел несколько многопоточных уроков (никогда не использовал его раньше). Но потом мне пришло в голову, что я мог бы просто иметь логический переключатель в обработчике событий, на который смотрят несколько ifs, разбросанных по длине метода, который нужно прервать. Если true, он выдает исключение, перехватываемое попыткой/перехватом всего метода. В улове он перестраивает графический интерфейс с входными данными, измененными с помощью щелчка мыши. Гораздо проще :-) - person ZamielTheGreat; 29.01.2012
comment
Если у вас есть логическое значение, которое должно быть прочитано многими потоками, вы должны пометить его как volatile. ;-) - person AppleGrew; 29.01.2012

Вы смотрели SwingWorker? Это простая структура, позволяющая выполнять вычисления в фоновом режиме и периодически публиковать обновления в потоке графического интерфейса.

person casablanca    schedule 28.01.2012
comment
@Hovercraft: в вопросе четко упоминается, что я использую качели. (по крайней мере, с тех пор, как ОП отредактировал вопрос после вашего первоначального комментария) - person casablanca; 28.01.2012