소프트웨어 사용자들은 결코 메뉴얼을 안본다. 아무리 어려운 프로그램이라 해도 사용자는 자기 스스로 방법을 찾아내려고 한다. 정말 혼자서 해결하지 못한다 해도 다른 사람에게 물어볼지언정 메뉴얼은 안본다.

따라서 개발자는 프로그램을 사용하기 쉽고 직관적으로 이용할 수 있도록 만들어야 한다. 결코 메뉴얼을 보지 않는 사용자들에게 어떻게 프로그램을 이해시키고 도움을 줄 수 있을지, 몇가지 방법을 알아보자.

사용자 가이드하기
가장 자주 쓰이는 방법은 각 인터페이스에 간단한 힌트나 길잡이 문구를 넣는 것이다. 스윙(Swing) 프레임워크는 툴팁 형태로 이러한 기능을 적용시키도록 해준다. setToolTipText 메소드를 호출함으로써 어떤 스윙 컴포넌트에 대해서도 툴팁을 설정할 수 있다.

툴팁이 설정되면 마우스가 해당 컴포넌트 위로 위치할 경우 텍스트로 된 간단한 설명이 작은 윈도우에 표시된다(마우스가 지나가면 사라진다). 이 설명에는 컴포넌트의 기능이나 개발자가 유용하다고 생각하는 정보 등이 담겨있다.

툴팁은 간결하고 명확한 방법이지만 단점도 있다. 아주 소량의 정보만 전달할 수 있으며, 인터페이스 위에 겹쳐져서 표시되기 때문에 오히려 사용에 방해가 되기도 한다. 따라서 프로그램의 메뉴에 사용하기에는 적합하지 않은 면이 있다.

툴팁 외에 상태표시줄에 설명을 표시하는 방법이 있다. 기본 개념은 툴팁과 동일하다. 마우스가 어떤 요소 위에 있을 동안에만 설명이 나타난다. 다만 프로그램 윈도우의 상태표시줄에 설명이 보여지므로, 툴팁처럼 프로그램 사용에 방해가 되는 일은 없다.

마우스-오버 방식 적용하기
스윙에서는 상태표시줄에 설명을 보여주는 기능을 기본 제공되지 않지만, 어렵지 않게 직접 구현할 수 있다. 설명을 보여주고자 하는 각 항목을 '마우스 감지기(mouse listener)'와 연결하고, MouseListener 인터페이스의 mouseEntered와 mouseExited 메소드를 적용하면 된다.

mouseEntered 메소드는 설명을 보여주는 역할을, mouseExited 메소드는 설명을 사라지게 하는 역할을 한다. 이 이벤트 소스를 사용하면 항목을 식별해 자동으로 해당되는 설명을 보여주기 때문에 각 항목마다 일일이 마우스 연결을 하지 않아도 된다.

MouseOverHintManager(소스보기1)는 재사용이 가능한 상태표시줄 설명 기능을 보여준다. 사용법은 아주 간단하다. JLabel을 거치는 MouseOverHintManager의 인스턴스를 설정한 후, addHintFor 메소드를 호출해 각 항목에 대한 설명을 설정한다. 탑컨테이너(프로그램 창, 프레임, 대화상자 등)에 대해서 enableHints 메소드를 호출한다.

소스보기 1
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;


public class MouseOverHintManager implements MouseListener {
  private Map hintMap;
  private JLabel hintLabel;

  public MouseOverHintManager( JLabel hintLabel ) {
    hintMap = new WeakHashMap();
    this.hintLabel = hintLabel;
  }

  public void addHintFor( Component comp, String hintText ) {
    hintMap.put( comp, hintText );
  }

  public void enableHints( Component comp ) {
    comp.addMouseListener( this );
    if ( comp instanceof Container ) {
      Component[] components = ((Container)comp).getComponents();
      for ( int i=0; i<components.length; i++ )
       enableHints( components[i] );
    }
    if ( comp instanceof MenuElement ) {
      MenuElement[] elements = ((MenuElement)comp).getSubElements();
      for ( int i=0; i<elements.length; i++ )
       enableHints( elements[i].getComponent() );
    }
  }

  private String getHintFor( Component comp ) {
    String hint = (String)hintMap.get(comp);
    if ( hint == null ) {
      if ( comp instanceof JLabel )
       hint = (String)hintMap.get(((JLabel)comp).getLabelFor());
      else if ( comp instanceof JTableHeader )
       hint = (String)hintMap.get(((JTableHeader)comp).getTable());
    }
    return hint;
  }


  public void mouseEntered( MouseEvent e ) {
    Component comp = (Component)e.getSource();
    String hint;
    do {
      hint = getHintFor(comp);
      comp = comp.getParent();
    } while ( (hint == null) && (comp != null) );
    if ( hint != null )
      hintLabel.setText( hint );
  }

  public void mouseExited( MouseEvent e ) {
    hintLabel.setText( " " );
  }

  public void mouseClicked( MouseEvent e ) {}
  public void mousePressed( MouseEvent e ) {}
  public void mouseReleased( MouseEvent e ) {}
}


MouseOverHintManager를 적용하는 것도 상기한 절차와 유사하다. addHintFor 메소드는 변수로 컴포넌트 레퍼런스와 이에 대응하는 설명을 받고, 이를 맵(Map)에 저장한다. 설명은 WeakHashMap 인스턴스에 저장되기 때문에 대응 항목에 대한 참조가 더 이상 없을 경우 자동적으로 가비지-콜렉트(garbage-collected) 처리된다. 따라서 설명을 사라지게 하기 위한 별도의 메소드는 필요 없다.

enableHints 메소드는 변수인 탑컨테이너의 모든 항목, 하부항목, 메뉴요소 등에 대한 마우스 감지기로 MouseOverHintManager를 추가한다.

mouseEntered 메소드는 마우스 포인터가 항목을 가리키면 맵에서 해당되는 설명을 찾아 JLabel에 뿌려준다. mouseExited 메소드는 JLabel을 비워 설명을 제거한다.

트릭
상태표시줄 설명과 관련된 몇가지 간단한 트릭이 있다.

우선 mouseEntered 메소드는 ‘이벤트를 생성하는 항목’의 설명을 받는다는 점을 이용한다. mouseEntered는 해당 항목에 대응하는 설명이 없으면 상위 항목을 확인한다. 이 프로세스는 최상위 항목에 도달하거나 설명이 발견될 때까지 계속된다. 이 특성을 이용해 패널과 같은 하나의 컨테이너에 대해 하위 모든 요소들이 같은 설명을 공유하도록 하거나, 또는 상위 항목과 하위 항목에 대해 서로 다른 설명을 설정할 수 있다.

또다른 트릭은 getHintFor 메소드다. mouseEntered는 항목에 대한 설명을 받기위해 getHintFor를 호출한다. 그런데 변수로 넘겨받은 항목에 설명이 존재하지 않는 경우, getHintFor는 몇가지 특별한 경우를 체크한다. 문제의 항목이 JLabel, 또는 JTableHeader일 경우, getHintFor 메소드는 각각 JLabel이 붙여진 설명과 Jtable 설명을 리턴한다. 즉 항목과 라벨에 대해 별도로 두 번 설명을 설정할 필요가 없는 것이다. 각자 서로 다른 항목으로 보여지는 경우라도 마찬가지다.

설명을 넣자
MouseOverHintDemo(소스보기2)는 MouseOverHintManager 클래스 사용에 대한 간단한 예로, 상태표시줄이 있는 JFrame을 생성한다. 이 JFrame은 가장 흔히 사용되는 스윙 요소 및 설명을 보여준다.

소스보기 2
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;


class MouseOverHintDemo {
  public static void main( String[] args ) {
    JLabel hintBar = new JLabel(" ");
   MouseOverHintManager hintManager = new MouseOverHintManager(hintBar);
    JFrame frame = new JFrame("MouseOverHintDemo");
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  
    JMenuBar menuBar = new JMenuBar();
    JMenu menu = new JMenu("File");
    JMenuItem item1 = new JMenuItem("Load");
    JMenuItem item2 = new JMenuItem("Save");
    JMenuItem item3 = new JMenuItem("Exit");
  
    Box mainPanel = Box.createVerticalBox();
    JButton button = new JButton("Apply");
    JCheckBox checkBox = new JCheckBox("Disable hints");
    JLabel label = new JLabel("Backup strategy");
    JComboBox comboBox = new JComboBox(new String[] {"Always","Just the last","Never"});
    JFormattedTextField formattedText = new JFormattedTextField(new Date());
    Box radioPanel = Box.createVerticalBox();
    ButtonGroup radioGroup = new ButtonGroup();
    JRadioButton radio1 = new JRadioButton("left");
    JRadioButton radio2 = new JRadioButton("right");
    JTable table = new JTable(new String[][] {{"Copy","Ctrl+C"},{"Paste","Ctrl+V"},{"Cut","Ctrl+X"}}, new String[] {"Action","Shortcut"});
  
    hintManager.addHintFor( item1, "Loads a new file" );
    hintManager.addHintFor( item2, "Saves the current file" );
    hintManager.addHintFor( item3, "Exits the application" );
    hintManager.addHintFor( button, "Apply any changes made" );
    hintManager.addHintFor( checkBox, "Turns off the display of hints" );
    hintManager.addHintFor( comboBox, "Selects how many backups to make" );
    hintManager.addHintFor( formattedText, "Enters the date for next run" );
    hintManager.addHintFor( radioPanel, "Selects the position for application's toolbar" );
    hintManager.addHintFor( table, "Shortcuts for each application's action" );
  
    frame.setJMenuBar( menuBar );
    menuBar.add( menu );
    menu.add( item1 );
    menu.add( item2 );
    menu.add( item3 );
    frame.getContentPane().add( mainPanel, BorderLayout.CENTER );
    mainPanel.add( Box.createVerticalStrut(5) );
    mainPanel.add( button );
    mainPanel.add( Box.createVerticalStrut(5) );
    mainPanel.add( checkBox );
    mainPanel.add( Box.createVerticalStrut(5) );
    mainPanel.add( label );
    label.setLabelFor( comboBox );
    mainPanel.add( comboBox );
    mainPanel.add( Box.createVerticalStrut(5) );
   mainPanel.add( formattedText );
    mainPanel.add( Box.createVerticalStrut(5) );
    radioGroup.add( radio1 );
    radioGroup.add( radio2 );
    radioPanel.setBorder( BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),"Toolbar") );
    radioPanel.add( radio1 );
    radioPanel.add( radio2 );
    mainPanel.add( radioPanel );
    mainPanel.add( Box.createVerticalStrut(5) );
    mainPanel.add( table.getTableHeader() );
    mainPanel.add( table );
    mainPanel.add( Box.createVerticalStrut(5) );
    hintBar.setBorder( BorderFactory.createLoweredBevelBorder() );
    frame.getContentPane().add( hintBar, BorderLayout.SOUTH );
    frame.pack();
  
    hintManager.enableHints( frame );
    frame.setVisible( true );
  }
}


이 예제는 단순명료하다. 사용자 인터페이스의 모든 항목을 생성하고 이들에 대해 설명을 설정한 후 애플리케이션 윈도우를 만들고 ouseOverHintManager.enableHints를 호출한다. 유의할 점은 유저 인터페이스를 구성한 후에 끝으로 enableHints를 호출해야 enableHints가 모든 항목에 대한 마우스 감지기로 등록된다는 것이다. 이렇게 해야 MouseOverHintManager가 모든 항목의 마우스 이벤트를 접수해 각각에 해당하는 설명을 선택할 수 있다.

소스코드2 예제를 실행하면 몇가지 조절기능을 갖춘 간단한 윈도우가 뜬다. 항목과 메뉴 위로 마우스를 움직이면서 상태표시줄에 각각에 해당되는 설명이 나타나는지 확인해 보자. 2개의 라디오 버튼에 대해서는 같은 설명이 표시되는데, 이는 상위 패널에서 설명을 설정했기 때문이다.

기능에 대한 설명은 애플리케이션 사용을 쉽게 한다는 점에서 중요한 부분이다. 스윙 툴팁의 대안인 상태표시줄 설명 방법은 적용하기도 쉽다. 사용자들은 결코 메뉴얼을 보지 않기 때문에 프로그램을 사용하기 쉽고 직관적으로 이용할 수 있도록 만들어야 하는 것이 개발자들의 임무지만, 자바 GUI를 사용하는 경우라면 상기한 상태표시줄 설명이라는 간단한 도움기능을 넣을 수 있을 것이다. @


내 남자의 길~! 블로그를 구독하고 싶으시면 Click --->


1 ··· 575 576 577 578 579 580 581 582 583 ··· 717 

글 보관함

카운터

Total : 1,676,592 / Today : 141 / Yesterday : 230
get rsstistory!