Я пытался понять, как именно работает объект JButton.
Я создал приведенный ниже объект SampleFrame, который расширяет JFrame.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class SampleFrame extends JFrame implements ActionListener{
SampleFrame() {
JButton button = new JButton("Hello");
add(button);
button.addActionListener(this);
}
@Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Hello");
}
public static void main(String[] args) {
new SampleFrame().setVisible(true);
}
}
Я понимаю, что add(button) добавляет объект JButton в JFrame, а button.AddActionLister(this) добавляет объект SampleFrame к набору прослушивателей этого события, поэтому его метод actionPerformed вызывается каждый раз при нажатии кнопки. Чего я не понимаю, так это как именно называется этот метод actionPerformed? Это какой-то поток, где это вызывается? Извините, если это звучит слишком наивно, я не слишком знаком с Java (и потоками).
Не уверен, имеет ли это какое-либо отношение к этому, но когда я закрываю фрейм, код все равно отображается в консоли как работающий. Это указывает мне на то, что параллельно выполняется своего рода цикл while.
См. также Начальные темы . На практике позвольте вашему отладчику прервать оператор печати, чтобы увидеть, что его вызвало. Затем отредактируйте свой вопрос, чтобы отразить пересмотренное понимание.




Давайте перепишем ваш пример кода, чтобы он соответствовал обычному подходу приложения Swing, как показано в классе HelloWorldSwing из руководства Swing, бесплатно предоставляемого Oracle.
public class OneButtonSwingApp
{
/**
* Create & show GUI. For thread safety, invoke from the event-dispatching thread.
*/
private static void createAndShowGUI ( )
{
// Create and set up the window.
JFrame frame = new JFrame( "One Button Example" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
// Widgets
JLabel timeLabel = new JLabel( "Now: " + Instant.now( ) );
JButton tellTimeButton = new JButton( "Tell time" );
// Behavior
tellTimeButton.addActionListener( ( ActionEvent actionEvent ) -> { timeLabel.setText( "Now: " + Instant.now( ) ); } );
// Arrange
frame.setLayout( new FlowLayout( ) );
frame.getContentPane( ).add( timeLabel );
frame.getContentPane( ).add( tellTimeButton );
// Display the window.
frame.pack( );
frame.setVisible( true );
}
public static void main ( String[] args )
{
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(
new Runnable( )
{
public void run ( )
{
createAndShowGUI( );
}
} );
}
}
Каждое Java-приложение запускается в потоке для выполнения метода main.
Приложения Swing имеют дополнительный поток. Эта тема посвящена запуску графического интерфейса. См. учебник Oracle: Поток отправки событий.
В нашем коде здесь, в нашем начальном потоке приложения, мы вызываем SwingUtilities.invokeLater, чтобы запланировать позднее выполнение некоторого кода в этом потоке диспетчеризации событий. В рамках этого вызова мы передаем объект анонимного класса, реализующего интерфейс Runnable. Реализация метода run необходима для выполнения контракта Runnable.
В современной Java мы бы упростили этот main метод с помощью ссылки на метод .
public static void main ( String[] args )
{
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater( OneButtonSwingApp :: createAndShowGUI );
}
В Swing, как и в любой другой среде графического интерфейса, которую я когда-либо видел, вы никогда не получаете доступ к элементам графического интерфейса и не манипулируете ими из другого потока. Всегда используйте поток диспетчеризации событий для запуска любого кода для доступа к элементам графического интерфейса и манипулирования ими.
добавление объекта SampleFrame в набор прослушивателей этого события, чтобы его метод actionPerformed вызывался каждый раз при нажатии кнопки
Да, наш вызов tellTimeButton.addActionListener передает лямбду (код, который будет выполнен позже) коллекции слушателей. Вызов этого метода означает, что мы сообщаем кнопке «Вот код, который будет запускаться всякий раз, когда происходит событие действия».
В своем коде вы передали сам экземпляр SampleFrame в качестве слушателя. Хотя создание прослушивателя объекта JFrame может работать, я бы не рекомендовал этого делать. Держите свои объекты четко очерченными, каждый из которых имеет единственную причину существования. Создайте отдельный объект с поведением, которое вы хотите запускать в ответ на нажатие кнопки.
Позже, во время выполнения, платформа Swing отслеживает действия пользователя. Когда пользователь нажимает эту кнопку, кнопка проходит через свою коллекцию прослушивателей, чтобы выполнить приведенный ранее код. Код выполняется в потоке диспетчеризации событий. Поэтому крайне важно, чтобы ваш код прослушивателя был коротким и понятным, поскольку пользовательский интерфейс зависает/перестает отвечать на запросы во время выполнения этого кода. Если вам предстоит выполнить длительную задачу, перейдите к другому потоку с помощью SwingWorker.
Чего я не понимаю, так это того, как именно вызывается этот метод actionPerformed?
Swing — это 👉🏽событийно-управляемая структура. Это означает, что структура отвечает за мониторинг событий, поступающих от пользователя или из других источников. Поток управления в вашем приложении не является главным. Вы заранее настраиваете прослушиватели, а затем код прослушивателя выполняется по мере возникновения конкретных событий.
Вам действительно стоит изучить Учебное пособие Oracle по Swing, чтобы понять эти ключевые понятия.
Последующие действия. Могу ли я использовать Swing, но удалить «поток отправки событий» и написать свой собственный код, который будет выполнять все задачи, связанные с графическим интерфейсом, в том же потоке, что и основной метод? Раньше я использовал pygame (на Python) для создания графических интерфейсов, и мне не приходилось беспокоиться о других потоках, работающих параллельно. Спрашиваю об этом, потому что мне бы хотелось максимально избежать многопоточности. Я также открыт для изучения других альтернатив Swing.
@UmangLunia Нет, вы не можете/не должны пытаться победить поток отправки событий в Swing. Чтобы запустить код в потоке отправки событий, просто прикрепите прослушиватели к виджетам Swing. Например, см. вызов tellTimeButton.addActionListener в примере выше. Переданная туда лямбда действительно будет выполнена в потоке отправки событий. Остерегайтесь: как обсуждалось в ответе, пользовательский интерфейс зависает (не отвечает) пользователю, пока такой код выполняется в потоке отправки событий. Только для долго выполняющегося кода вам понадобится еще один поток. Опять же, поработайте с учебником Oracle.
Вы можете значительно облегчить себе задачу, исключив из этого сценария все потоки. Отдельных тем нет. Фактически, вся деятельность Swing происходит в одном и только одном потоке, поэтому вам вообще не нужно об этом беспокоиться. Собственный код JButton (или, по крайней мере, некоторые внутренние классы, которые использует JButton) будет явно просматривать список добавленных прослушивателей ActionListener всякий раз, когда пользователь активирует кнопку, и вызывать метод actionPerformed каждого такого прослушивателя.