저자: Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole, 역 한빛리포터 이상화
본 기사는『Java Swing, 2nd Edition』에서 메뉴와 툴바에 대한 내용을 다룬 챕터를 요약한 기사이다. 이번에는 JMenuBar 클래스를 가지고 메뉴 바 선택 모델에 대해 배워보겠다.
이전 기사보기: 자바 스윙: 메뉴와 툴바 - 제 1편
『Java Swing, 2nd Edition』에서 메뉴와 툴바에 대한 내용을 다룬 챕터를 요약한 기사로 스윙 메뉴에 대한 설명을 하고 있다.
메뉴 바 선택 모델
대부분의 GUI 환경에서 메뉴 컴포넌트는 한 번에 오직 한번의 선택만 이루어 진다. 스윙 또한 예외는 아니다. 스윙은
SingleSelectionModel 인터페이스를 통해 메뉴 바와 메뉴가 한번의 선택을 하는 데이터 모델을 제공한다.
SingleSelectionModel 인터페이스
SingleSelectionModel 인터페이스를 상속한 객체들은 선택 가능한 것을 배열로 유지하면서 한번에 하나의 배열 요소만 선택될 수 있다. 데이터 모델은 선택된 요소의 인덱스를 가지고 있는데 만약 새로운 요소가 선택된다면 데이터 모델은 선택된 요소를 나타내는 인덱스를 초기화 하고 개개의 등록된 리스너(listener)들로 ChangeEvent를 발생 시킨다.
속성
SingleSelectionModel 인터페이스를 상속한 객체들은 [표 14-1]과 같은 속성들을 가지고 있다.
Selected 속성은 선택 여부를 알려주는
boolean 타입이며
selectedIndex 속성은 현재 선택된 아이템을 나타내는
int 타입의 인덱스이다.
[표 14-1]
SingleSelectionModel 속성
속성명 |
데이터 형 |
get |
is |
set |
기본값 |
selected |
boolean |
|
· |
|
|
selectedIndex |
int |
· |
|
· |
|
이벤트
SingleSelectionModel 인터페이스를 상속한 객체들은 객체가
selectedIndex 속성을 변경할 때(즉, 선택이 변경될 때) 반드시
ChangeEvent를 발생시켜야 한다. 이 인터페이스는
ChangeEvent 리스너들의 리스트 관리를 위한
addChangeListener( ),
removeChangeListener( ) 메소드를 포함하고 있다.
이것을 통해 모델 변경 이벤트를 받으면 리스너들의 리스트로부터 지정된
ChangeListener를 추가하거나 변경할 수 있다.
void addChangeListener(ChangeListener listener)
void removeChangeListener(ChangeListener listener)
메소드
SingleSelectionModel 인터페이스는 하나의 메소드를 포함하고 있다. 선택된 값을 지우기 위해서는
selected 속성을
false로 변경하면 된다.
public void clearSelection()
DefaultSingleSelectionModel 클래스
스윙은
SingleSelectionModel 인터페이스의 요약 구현으로
DefaultSingleSelectionModel 클래스를 제공하고 있다.
속성
DefaultSingleSelectionModel은 [표 14-2]와 같이
SingleSelectionModel 인터페이스에 의해 상속받은 속성을 포함하고 있다.
SelectedIndex 속성은 현재 선택된 아이템을 나타내는 정수형 인덱스이다. 기본값
-1은 어떠한 선택도 없다는 것을 나타낸다.
Selected 속성은
SelectedIndex가
-1 이상 일 때
true 그렇지 않으면
false를 나타내는
boolean 값이다.
[표 14-2]
DefaultSingleSelectionModel 속성
속성명 |
데이터 형 |
get |
is |
set |
기본 값 |
selected |
boolean |
|
· |
|
false |
selectedIndex |
int |
· |
|
· |
-1 |
이벤트와 메소드들
DefaultSingleSelectionModel 객체는 앞서 언급한
SingleSelectionModel 인터페이스의 모든 이벤트와 메소드들을 제공한다.
JMenuBar 클래스
스윙의
JMenuBar 클래스는 AWT
MenuBar 클래스를 대체하고 있다. 이 클래스는 하나 이상의 메뉴를 추가할 수 있는 수평 메뉴 바 컴포넌트를 만든다.
JMenuBar 클래스는 사용자가 메뉴를 한 순간에 한번만
활성화시키거나, 펼치기 때문에
DefaultSingleSelectionModel을 사용한다. 일단 마우스 포인터가 메뉴를 지나치면 클래스는 스크린으로부터 메뉴를 제거하고 모든 메뉴들은 다시 선택의 대상이 된다. [그림 14-1]은
JMenuBar 컴포넌트의 클래스 계층구조를 보여주고 있다.
[그림 14-4] JMenuBar 클래스 다이어그램
JMenuBar 클래스의
add( ) 메소드를 이용하여 메뉴 바에
JMenu 객체를 추가할 수 있다.
JMenuBar는 추가된 메뉴에 순차적인 순서로 정수형의 인덱스를 할당한다. 메뉴 바는 할당된 인덱스를 가지고 왼쪽에서 오른쪽으로 메뉴를 화면에 나타낸다. 이론적으로 help 메뉴는 예외인데 반드시 하나의 메뉴를 help 메뉴로 지정하면
JMenuBar는
Error를 발생시킨다. († 역자 주: 추가적인 설명이 이후에 존재한다.)
메뉴 바 위치
2가지 방법 중 하나를 통해서 애플릿이나 스윙 프레임에 메뉴 바를 붙일 수 있다. 우선은
JFrame,
JDialog,
JApplet,
JinternalFrame의
setJMenuBar( ) 메소드를 사용할 수 있다.
JFrame frame = new JFrame("Menu");
JMenuBar menuBar = new JMenuBar( );
// 프레임에 메뉴 바 붙이기
frame.setJMenuBar(menuBar);
setJMenuBar( ) 메소드는
java.awt.Frame의
setMenuBar( ) 메소드와 유사하다. 이 메소드는 보통 프레임 내부의
Insets에 따라 프레임 상단의 메뉴 바에 위치하는 메뉴의 위치를 Look & Feel에 맞추어 나타낼 수 있다.
JApplet와
JDialog는
setJMenuBar( )를 가지고 있다. 이것은 애플릿과 다이얼로그 박스에 메뉴 바를 추가할 수 있다는 뜻이다. 어느 방법이든 스윙 메뉴를 쓰거나 컴파일할 때 오류가 생긴다면 AWT의
setMenuBar( )와
setJMenuBar( )를 혼동했을 것이다.
애플리케이션이 매킨토시에서 작동할 때 사용자들이 쉽게 찾을 수 있는 화면 상단에 메뉴 바를 오게 하려면 Look & Feel을 조정해야 한다. 시스템 속성
com.apple.macos.useScreenMenuBar를
true로 설정하면 이와 같은 동작이 활성화된다. 자바 프로그램에서는 이것을 기본적으로 지원하지 않기 때문에 반드시 코드로 명시되어 있어야 한다. 맥의 Aqua Human Interface는 명시적으로 메뉴 바가 항상 보이도록 요구하고 있다. 어느 애플리케이션이든지 메뉴 바가 부족한 프레임을 가지고 있다면 프레임 중 하나가 포커스를 얻게 될 때마다 메뉴 바가 사라질 것이다. 이러한 문제점에 대해 일반적인 해결책은 각 프레임 당 동일한 메뉴 바를 만들 수 있는 메뉴 팩토리를 이용하는 것이다. 비록 추가적인 작업이 필요하지만 맥 사용자들에게는 유용할 것이다.
다음으로 메뉴 바를 추가하는 방법은 그리 사용빈도가 많지는 않다.
Jcomponent를 상속 받는
JmenuBar를 다시 생각해 보자. 이것은 다른 스윙 컴포넌트처럼 스윙 레이아웃 관리자의 영향을 받는 것을 의미 하는 것이다. 예를 들어 다음 코드처럼
setJMenuBar( )를 대체할 수도 있다.
menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));
frame.getContentPane( ).add(menuBar, BorderLayout.SOUTH);
이것은 [그림 14-5]와 같이 메뉴 바를 프레임 바닥에 오게끔 한다. 메뉴 바의 위치를 잘 보여주기 위해서 메뉴 바 주위를 음영처리 하였다. 심지어는 2-3개의 메뉴 바를 서로 다른 위치에 추가할 수도 있기 때문에 반드시 프레임 상단에 메뉴 바를 고정시켜야 하는 것은 아니며 메뉴 바가
Jcomponent를 상속했기 때문에 여러 개의 메뉴 바는 컨테이너 내의 다양한 위치에서 보여질 수 있다.
[그림 14-5] 스윙 컴포넌트로의 JMenuBar 위치 시키기
팁: 두꺼운 효과를 줄려면 메뉴 바에 적어도 하나의 이름을 가진 메뉴를 추가해야 한다. 그렇지 않으면 분리자와 비슷하게 얇은 선처럼 나타난다.
물론 실제로 이런 경우는 필요에 의해서가 아니면 드문 경우이며 적절한 위치에 메뉴 바를 위치시키기 위한 Look & Feel을 무시하는 것이다. 여러 개의 메뉴 바를 생성하면 혼란스러운 것과 같이 메뉴 바와 같은 기본적인 것에 대한 변경은 혼동을 일으킬 뿐만 아니라 유용성 측면에서도 좋지 않다.
속성
JMenuBar 클래스의 속성은 [표 14-3]에 정리되어 있다.
menu 속성은 메뉴 바에 붙은
JMenu를 참고하는 인덱스된 속성이다. 읽기 속성인
menuCount 속성은 메뉴 바에 추가된 메뉴의 총 개수를 가지고 있는 정수 값이다. 한번에 하나의 메뉴만 선택되거나 활성화 되는 모델을 생각해 보자. 어떤 메뉴든지 현재 활성화되어 있다면
selected 속성은
true, 그렇지 않다면
false이다.
componentAtIndex 속성은 주어진 인덱스를 가지고 메뉴에 접근 할 수 있다. 내용이
Component로 캐스트 된다는 것만 제외하면
menu 속성과 유사하다. 주어진 인덱스와 컴포넌트가 연결되지 않았다면
getComponentAtIndex( ) 접근자는
null을 리턴한다.
component 속성은 메뉴 바 자체인
this의 참조자를 리턴한다.
SubElements는 메뉴 바 상에 있는 메뉴들의 배열을 리턴한다.
[표 14-3] JMenuBar 속성
속성명 |
데이터 형 |
get |
is |
set |
기본값 |
accessibleContexto |
AccessibleContext |
· |
|
|
JMenuBar.AccessibleJMenuBar( ) |
borderPaintedb |
boolean |
|
· |
· |
true |
component |
Component |
· |
|
|
this |
componentAtIndexi |
Component |
· |
|
|
true |
helpMenuu |
JMenu |
· |
|
· |
Throws an Error |
layouto |
LayoutManager |
· |
|
· |
BoxLayout(X_AXIS) |
marginb |
Insets |
· |
|
· |
null |
menuCount |
int |
· |
|
|
0 |
menui |
JMenu |
· |
|
|
null |
selected |
boolean |
|
· |
|
false |
selectionModelb |
SingleSelectionModel |
· |
|
· |
DefaultSingleSelectionModel( ) |
subElemens |
MenuElement[ ] |
· |
|
|
|
UIb |
MenuBarUI |
· |
|
· |
From L&F |
UIClassIDo |
string |
· |
|
|
"MenuBarUI" |
bbound, iindexed, ooverridden, uunimplemented
[표 3-6]의 JComponent 클래스 참고 |
margin 속성은 메뉴 바 경계와 메뉴 사이의 공간을 조절한다.
border 속성이
null값이 아니더라도
borderPainted 속성을 통해 메뉴 바 경계의 화면 출력을 조절할 수도 있다.
borderPainted를
false로 설정하면 경계의 일반적인 화면 출력을 막을 수 있다. 스윙 border에 관해 알고 싶다면
『Java Swing, 2nd Edition』의 Chapter 13을 참고하기 바란다.
주의: helpMenu 속성은 OS에 따라 특별한 위치를 가지는 help 메뉴를 지정할 수 있지만 불행히도 이 속성은 작동하지 않으며 SDK 1.4에서는
Error를 발생시킨다.
BoxLayout의 glue기능을 이용하여 help 메뉴를 오른쪽 끝에 오게 할 수도 있겠지만 현재의 Look & Feel에 비해 불필요하게 코드를 증가시키게 될 것이다.
생성자
public JMenuBar( )
JMenuBar 객체를 초기화 하고 생성 시킨다.
메뉴
public JMenu add(JMenu menu)
이 메소드를 사용하여
JMenu를 메뉴 바에 추가 시킬 수 있다.
JMenuBar의
BoxLayout때문에
add( )를 사용하여 추가한 순서대로 왼쪽부터 오른쪽으로 메뉴가 배열될 것이다. 또한 이 메소드는 추가된
JMenu의 참조자를 리턴하기 때문에 다음과 같은 호출도 가능하다.
menubar.add(menu).add(menuitem)
기타
public int getComponentIndex(Component c)
매개변수로 넘겨진 Component의 인덱스를 리턴한다. 만약 일치하는 컴포넌트가 없다면
-1을 리턴한다. 매개변수로 넘어온 컴포넌트의 유일한 타입은 JMenu이다.
public void setSelected(Component c)
메뉴 바가 특정한 메뉴를 선택하게 함으로서
ChangeEvent를 발생한다. 이 메소드는 밑줄이 있는 단축키를 통해 특정한 메뉴가 선택되게 하는데 사용하게 된다. 이것은 [표 14-3]에 있는
boolean selected 속성과는 다르다.
public void updateUI( )
UIManager가 현재 UI 대리자에 기초를 두고 있는 컴포넌트의 Look & Feel를 갱신한다.
MenuElement 인터페이스를 상속 받은
JmenuBar의 메소드는 다음 기사에서 다룰 것이다.