자바

자바 UI - 03) Swing (이벤트처리기, 버튼 컴포넌트)

록's 2023. 2. 16. 10:23
728x90
반응형




이벤트 처리기

UI 프로그램은 사용자와 상호작용을 하면서 코드를 실행한다. 사용자가 UI의 컴포넌트를 사용하는 순간 이벤트(event)가 발생하고, 프로그램은 이벤트를 처리하기 위해 코드를 실행한다.

 

이벤트 소스 객체(컨테이너, 컴포넌트)와 이벤트 처리 객체(리스너Listener)를 분리하는 위임형(Delegation) 방식을 사용

 

컴포넌트는 하나의 이벤트만 발생하는 것이 아니라 동시에 여러 개의 이벤트가 발생하기도 한다.

  • 예) JButton을 마우스로 클릭하면 액션 이벤트ActionEvent와 함께 마우스 이벤트MouseEvent도 발생 한다. 액션 이벤트는 마우스로 클릭하거나 Enter 키를 눌러 사용하는 컴포넌트에서 주로 발생하고, 마우스 이벤트는 대부분의 컨테이너 또는 컴포넌트에서 발생한다.

 

 

 

이벤트 소스에 리스너를 추가하려면 addXXXListener() 메소드를 사용

 

  • 예) WindowEvent, ActionEvent, ListSelectionEvent를 처리하기 위한 리스너를 추가하는 메소드
jFrame.addWindowListener(WindowListener listener);
jButton.addActionListener(ActionListener listener);
jList.addListSelectionListener(ListSelectionListener listener);

 

 

 

Listener와 Adapter

 

리스너 클래스를 생성하는 방법은 리스너 인터페이스를 구현하는 방법과 어댑터 클래스를 상속하는 방법이 있다.

 

 

 

리스너 인터페이스를 구현하는 방법

 

리스너 인터페이스를 구현하려면 리스너 인터페이스에 정의되어 있는 이벤트 처리 메소드를 모두 재정의해야 한다.

 

  • 예) WindowEvent가 발생했을 때 사용자의 행위에 따라 개별적으로 실행
  • 윈도우 상단 우측 닫기(×) 버튼을 클릭하면 windowClosing() 메소드가 실행되고, 최소 화(_) 버튼을 클릭하면 windowIconified() 메소드가 실행
  • 닫기(×) 버튼에서 발생하는 WindowEvent만 처리하고 싶어도 windowClosing() 및 나머지 6개를 다음과 같이 모두 재정의
class MyWindowListener implements WindowListener {
public void windowActivated(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowClosing(WindowEvent e) {
 //닫기(x) 버튼을 클릭했을 때 처리 방법 코딩
}
public void windowDeactivated(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
}

 

 

 

리스너 어댑터를 상속하는 방법

 

리스너 어댑터를 상속하면 관심 있는 이벤트 처리 메소드만 재정의할 수 있기 때문에 리스너 인터페이스를 구현하는 방법보다 좀 더 효율적이다.

 

 

  • 예) WindowAdapter 클래스를 상속할 경우 windowClosing() 메소드만 재정의하면 된 다. 나머지 6개의 메소드는 WindowAdapter에서 내용이 없는 채로 이미 구현되어 있기 때문
class MyWindowListener extends WindowAdapter {
  public void windowClosing(WindowEvent e) {
  //닫기(x) 버튼을 클릭했을 때 처리 방법 코딩
  }
}

 

 

예제 - JFrame의 제목 표시줄의 닫기(×) 버튼과 하단에 있는 btnClose 버튼 중 하나를 클릭하면 프로그램이 종료

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;


public class ClosableExample1 extends JFrame {
	private JButton btnClose;
	
	public ClosableExample1() {
		this.setTitle("CloseExample");
		this.setSize(300, 100);
		
		this.setLayout(new FlowLayout());
		this.getContentPane().add(getBtnClose());
		
		// WindowListener 추가
		this.addWindowListener(new MyWindowAdapter());
	}
	
	// 닫기 버튼 생성
	private JButton getBtnClose() {
		if(btnClose == null) {
			btnClose = new JButton();
			btnClose.setText("닫기");
			
			// 닫기 동작 리스너 추가 액션 리스너 - ActionListener 추가
			btnClose.addActionListener(new MyActionListener());
		}
		return btnClose;
	}
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
			ClosableExample1 jFrame = new ClosableExample1();
			jFrame.setVisible(true);
			}
		});
	}
}

// WindowAdapter 클래스를 상속해서 WindowListener 클래스 작성
class MyWindowAdapter extends WindowAdapter {
	@Override
	public void windowClosing(WindowEvent e) {
		System.exit(0);
	}
}

// ActionListener를 구현해서 ActionListener 클래스 작성
class MyActionListener implements ActionListener {
	@Override
	public void actionPerformed(ActionEvent e) {
		System.exit(0);
	}
}

- 실행 결과 -

왼쪽 상단에 생김.

 

 

 

 

 

Anonymous Listener

이벤트를 처리할 때 리스너 클래스를 외부 클래스로 선언하게 되면 컨테이너의 필드와 메소드에 접근하는 것이 불편하다. 그래서 리스너는 일반적으로 익명(Anonymous) 객체로 작성한다.

 

 

이전 예제를 수정한 것, 익명 객체를 사용해서 이벤트를 처리 방법

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class ClosableExample2 extends JFrame{
	private JButton btnClose;
	
	// 메인 윈도우 설정
	public ClosableExample2() {
		this.setTitle("CloseExample");
		this.setSize(300, 100);
		
		this.setLayout(new FlowLayout());
		this.getContentPane().add(getBtnClose());
		
		// WindowListener 추가
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
	}
	
	// 닫기 버튼 생성
	private JButton getBtnClose() {
		if(btnClose == null) {
			btnClose = new JButton();
			btnClose.setText("닫기");
			
			// 닫기 동작 리스너 추가 액션 리스너 - ActionListener 추가
			btnClose.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					System.exit(0);
				}
			});
		}
		return btnClose;
	}
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
			ClosableExample1 jFrame = new ClosableExample1();
			jFrame.setVisible(true);
			}
		});
	}
}

- 실행 결과 - 

 

 

 

 

복수 개의 컴포넌트에서 동일한 리스너를 사용해서 이벤트를 처리하고 싶다면 리스너를 필드 로 선언

 

 

두 개의 JButton에서 발생하는 ActionEvent를 하나의 ActionListener 익명 객체 로 처리하는 방법

  • 어떤 컴포넌트에서 ActionEvent가 발생되었는지 구분하기 위해 ActionEvent의 getSource() 메소드를 이용
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class ActionListenerExample extends JFrame{
	private JButton btnOk;
	private JButton btnCancel;
	
	// 메인 윈도우 설정
	public ActionListenerExample() {
		this.setTitle("ActionListenerExample");
		this.setSize(300, 100);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		this.setLayout(new FlowLayout());
		this.getContentPane().add(getBtnOk());
		this.getContentPane().add(getBtnCancel());
	}
	
	// ActionListener 타입의 필드 선언 및 익명 객체로 초기화
	private ActionListener actionListener = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent e) {
			//ActionEvent가 발생한 컴포넌트 구분
			if(e.getSource() == btnOk) {
				System.out.println("확인 버튼을 클릭했습니다.");
			} else if (e.getSource() == btnCancel) {
				System.out.println("취소 버튼을 클릭했습니다");
			}
		}
	};
	
	private JButton getBtnOk() {
		if(btnOk == null) {
			btnOk = new JButton();
			btnOk.setText("확인");
			// actionListener 필드 대입 , 버튼 동작
			btnOk.addActionListener(actionListener);
		}
		return btnOk;
	}
	
	// Cancel 버튼 생성
	private JButton getBtnCancel() {
		if(btnCancel == null) {
			btnCancel = new JButton();
			btnCancel.setText("취소");
			// actionListener 필드 대입
			btnCancel.addActionListener(actionListener);
		}
		return btnCancel;
	}
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
			ActionListenerExample jFrame = new ActionListenerExample();
			jFrame.setVisible(true);
			}
		});
	}
}

 

- 실행 결과 -

확인 버튼을 누르면 확인 버튼을 클릭했습니다 출력

취소 버튼을 누르면 취소 버튼을 클릭했습니다 출력

 

 

 

 

 

버튼 컴포넌트

버튼 컴포넌트는 AbstractButton을 상속받은 하위 클래스들을 말한다.

버튼 컴포넌트에는 JButton, JToggleButton, JRadioButton, JCheckBox가 있는데, 모두 사용자가 마우스로 클릭하여 사용 할 수 있도록 되어 있다.

 

버튼 컴포넌트를 마우스로 클릭하면 모두 ActionEvent가 발생한다. 그래서 addActionListener() 메소드로 ActionListener 객체를 등록하여 이벤트를 처리할 수 있다.

 

 

 

 

JButton

JButton은 이미지와 텍스트로 구성된 일반적인 버튼을 만들 때 사용한다. JButton의 setText() 메소드는 버튼의 텍스트를 설정하고, setIcon() 메소드는 버튼의 이미지를 설정한다

 

JButton jButton = new JButton();
jButton.setText("새문서");
jButton.setIcon( new ImageIcon( getClass().getResource("new.gif") );

 

 

텍스트, 이미지, 텍스트+이미지 버튼을 생성 

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class JButtonExample extends JFrame {
	private JButton btn1, btn2, btn3;
	
	// 메인 윈도우 설정
	public JButtonExample() {
		this.setTitle("JButtonExample");
		this.setSize(300, 100);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.getContentPane().setLayout(new FlowLayout());
		this.getContentPane().add(getBtn1());
		this.getContentPane().add(getBtn2());
		this.getContentPane().add(getBtn3());				
	}
	
	// 글자만 있는 버튼 생성
	public JButton getBtn1() {
		if(btn1 == null) {
			btn1 = new JButton();
			btn1.setText("새문서");
			btn1.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					JFileChooser jFileChooser = new JFileChooser();
					jFileChooser.showOpenDialog(JButtonExample.this);
				}
			});
		}
		return btn1;
	}
	
	// 아이콘만 있는 버튼 생성
	public JButton getBtn2() {
		if(btn2 == null) {
			btn2 = new JButton();
			btn2.setIcon(new ImageIcon(getClass().getResource("new.gif")));
			btn2.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					JFileChooser jFileChooser = new JFileChooser();
					jFileChooser.showOpenDialog(JButtonExample.this);
				}
			});
		}
		return btn2;
	}
	
	// 아이콘과 글자가 있는 버튼 생성
	public JButton getBtn3() {
		if(btn3 == null) {
			btn3 = new JButton();
			btn3.setText("새문서");
			btn3.setIcon(new ImageIcon(getClass().getResource("new.gif")));
			btn3.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					JFileChooser jFileChooser = new JFileChooser();
					jFileChooser.showOpenDialog(JButtonExample.this);
				}
			});
		}
		return btn3;
	}
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
			JButtonExample jFrame = new JButtonExample();
			jFrame.setVisible(true);
			}
		});
	}
}

- 실행 결과 -

 

 

 

 

 

JToggleButton

JToggleButton은 선택된 상태와 그렇지 않은 두 가지 상태를 가지는 버튼이다.

 

생성 방법은 JButton과 유사해서 텍스트와 이미지를 설정할 수 있다.

JToggleButton jToggleButton = new JToggleButton();
jToggleButton.setText("확인");
jToggleButton.setIcon(new ImageIcon( getClass().getResource("ok.gif") );

JToggleButton은 선택된 상태를 가지는 버튼이기 때문에 ActionListener보다는 다음과 같 이 ItemListener로 이벤트를 처리하는 것이 좋다. ItemEvent의 getStateChange() 메소드는 JToggleButton이 선택되었을 경우 ItemEvent.SELECTED 상수값을 리턴한다.

jToggleButton.addItemListener(new ItemListener() {
	@Override
	public void itemStateChanged(ItemEvent e) {
		if(e.getStateChange() = = ItemEvent.SELECTED) {
 		//선택된 상태
 		} else {
 	 	//해제된 상태
 		}
	}
});

 

JToggleButton은 단독으로도 사용 가능하지만, JToggleButton 두 개를 ButtonGroup에 포함 시키면 두 버튼을 배타적으로 선택할 수 있다. ButtonGroup 내부에서는 하나의 버튼만이 선택된 상태로 존재할 수 있기 때문

ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add( jToggleButton1 );
buttonGroup.add( jToggleButton2 );

 

 

[On] [Off] 토글 버튼과, 배타적으로 선택할 수 있는 [Start]와 [Stop] 버튼을 생성, [Start]와 [Stop] 버튼을 클릭하면 메시지 다이얼로그를 보여주는 방법

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;

public class JToggleButtonExample extends JFrame{
	private JPanel pFirst;
	private JPanel pSecond;
	private JToggleButton tbOnOff;
	private JToggleButton tbStart;
	private JToggleButton tbStop;
	
	// 메인 윈도우 설정
	public JToggleButtonExample() {
		this.setTitle("JToggleButtonExampe");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.getContentPane().setLayout(new GridLayout(2,1));
		this.getContentPane().add(getPFirst());
		this.getContentPane().add(getPSecond());
		this.pack();
	}
	
	public JPanel getPFirst() {
		if(pFirst == null) {
			pFirst = new JPanel();
			pFirst.add(getTbOnOff());
		}
		return pFirst;
	}
	
	public JPanel getPSecond() {
		if(pSecond == null) {
			pSecond = new JPanel();
			pSecond.setBorder(new TitledBorder("원하는 기능은?"));
			pSecond.add(getTbStart());
			pSecond.add(getTbStop());
			
			// 베타적 선택을 위한 ButtonGroup 생성 및 토글 버튼 추가
			ButtonGroup buttonGroup = new ButtonGroup();
			buttonGroup.add(getTbStart());
			buttonGroup.add(getTbStop());
		}
		return pSecond;
	}
	
	// On/Off 토글 버튼 생성
	public JToggleButton getTbOnOff() {
		if(tbOnOff == null) {
			tbOnOff = new JToggleButton();
			tbOnOff.setText("On");
			tbOnOff.addItemListener(new ItemListener() {
				@Override
				public void itemStateChanged(ItemEvent e) {
					if(e.getStateChange() == ItemEvent.SELECTED) {
						getTbOnOff().setText("Off");
					} else {
						getTbOnOff().setText("On");
					}
				}
			});
		}
		return tbOnOff;
	}
	
	// Start 버튼
	public JToggleButton getTbStart() {
		if (tbStart == null) {
			tbStart = new JToggleButton();
			tbStart.setText("Start");
			tbStart.setIcon(new ImageIcon(getClass().getResource("start.gif")));
			tbStart.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					JOptionPane.showMessageDialog(JToggleButtonExample.this, "Start");
				}
			});
		}
		return tbStart;
	}
	
	// Stop 버튼
	public JToggleButton getTbStop() {
		if(tbStop == null) {
			tbStop = new JToggleButton();
			tbStop.setText("Stop");
			tbStop.setIcon(new ImageIcon(getClass().getResource("stop.gif")));
			tbStop.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					JOptionPane.showMessageDialog(JToggleButtonExample.this, "Stop");
				}
			});
		}
		return tbStop;
	}
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
			JToggleButtonExample jFrame = new JToggleButtonExample();
			jFrame.setVisible(true);
			}
		});
	}
}

- 실행 결과 -

 

 

 

 

JRadioButton

JRadioButton은 JToggleButton의 하위 클래스로, 둥근 모양의 선택과 텍스트를 함께 보여주는 버튼이다. JRadioButton은 동일한 ButtonGroup에 포함되어 한 번에 하나의 JRadioButton만 선택된 상태를 가진다.

 

 

 

두 개의 JRadioButton 중에 하나를 선택하면 JLabel의 이미지가 변경 방법

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;

public class JRadioButtonExample extends JFrame{
	private JPanel radioPanel;
	private JRadioButton rbBird;
	private JRadioButton rbCat;
	private JLabel lblPicture;
	
	public JRadioButtonExample() {
		setTitle("JRadioButtonExample");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.getContentPane().add(getRadioPanel(), BorderLayout.WEST);
		this.getContentPane().add(getLblPicture(), BorderLayout.CENTER);
		pack();
	}
	
	public JPanel getRadioPanel() {
		if(radioPanel == null) {
			radioPanel = new JPanel();
			radioPanel.setLayout(new GridLayout(2,1));
			radioPanel.add(getRbBird());
			radioPanel.add(getRbCat());
			
			ButtonGroup group = new ButtonGroup();
			group.add(getRbBird());
			group.add(getRbCat());
			
		}
		return radioPanel;
	}
	
	public JRadioButton getRbBird() {
		if(rbBird == null) {
			rbBird = new JRadioButton();
			rbBird.setText("Bird");
			rbBird.setSelected(true);
			rbBird.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					getLblPicture().setIcon(new ImageIcon(getClass().getResource("Bird.gif")));
				}
			});
		}
		return rbBird;
	}
	
	public JRadioButton getRbCat() {
		if(rbCat == null) {
			rbCat = new JRadioButton();
			rbCat.setText("Cat");
			rbCat.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					getLblPicture().setIcon(new ImageIcon(getClass().getResource("Cat.gif")));
				}
			});
		}
		return rbCat;
	}
	
	public JLabel getLblPicture() {
		if(lblPicture == null) {
			lblPicture = new JLabel();
			lblPicture.setIcon(new ImageIcon(getClass().getResource("Bird.gif")));
		}
		return lblPicture;
	}
	
		public static void main(String[] args) {
			SwingUtilities.invokeLater(new Runnable() {
			public void run() {
			JRadioButtonExample jFrame = new JRadioButtonExample();
			jFrame.setVisible(true);
			}
		});		
	}
}

- 실행 결과 -

 

 

 

 

JCheckBox

JCheckBox는 사각형의 체크박스와 텍스트를 함께 보여주는 컴포넌트이다.

컴포넌트도 JToggleButton을 상속하며 체크와 언체크의 두 가지 상태를 갖는다.

JRadioButton과 차이점은 개별적으로 선택할 수 있다는 것이다.

 

 

JCheckBox 체크 상태에 따라 JLabel의 이미지를 변경 방법

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class JCheckBoxExample extends JFrame{
	private JPanel pWest;
	private JCheckBox cbGlasses;
	private JCheckBox cbHair;
	private JLabel lblPicture;
	
	
	public JCheckBoxExample() {
		this.setTitle("JCheckBoxExample");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.getContentPane().add(getPWest(), BorderLayout.WEST);
		this.getContentPane().add(getLblPicture(), BorderLayout.CENTER);
		this.pack();
	}
	
	// 서쪽에 부착할 JPanel 생성
	public JPanel getPWest() {
		if (pWest == null) {
			pWest = new JPanel(new GridLayout(2, 1));
			pWest.add(getCbGlasses());
			pWest.add(getCbHair());
		}
		return pWest;
	}
	
	public JCheckBox getCbGlasses() {
		if(cbGlasses == null) {
			cbGlasses = new JCheckBox();
			cbGlasses.setText("Glasses");
			cbGlasses.addActionListener(actionListener);
		}
		return cbGlasses;
	}
	
	public JCheckBox getCbHair() {
		if(cbHair == null) {
			cbHair = new JCheckBox();
			cbHair.setText("Hair");
			cbHair.addActionListener(actionListener);
		}
		return cbHair;
	}
	
	public JLabel getLblPicture() {
		if(lblPicture == null) {
			lblPicture = new JLabel();
			lblPicture.setIcon(new ImageIcon(getClass().getResource("geek.gif")));
		}
		return lblPicture;
	}
	
	private ActionListener actionListener = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent e) {
			if(cbGlasses.isSelected() && cbHair.isSelected()) {
				lblPicture.setIcon(new ImageIcon(getClass().getResource("geek-glasses-hair.gif")));
			} else if (cbGlasses.isSelected()) {
				lblPicture.setIcon(new ImageIcon(getClass().getResource("geek-glasses.gif")));
			} else if(cbHair.isSelected()) {
				lblPicture.setIcon(new ImageIcon(getClass().getResource("geek-hair.gif")));
			} else {
				lblPicture.setIcon(new ImageIcon(getClass().getResource("geek.gif")));
			}
		}
	};
	
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				JCheckBoxExample jFrame = new JCheckBoxExample();
				jFrame.setVisible(true);
			}
		});
	}
}

- 실행 결과 -

 




728x90
반응형