//--------------------------------------------------------
   아래의 환경에서 작성된 글입니다.

   java version "1.4.2_03"
   Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_03-b02)
   Java HotSpot(TM) Client VM (build 1.4.2_03-b02, mixed mode)

   작성자 : Y*eo*ri.S*e*o@gmail.com (별표 제거)
   일   자 : 2005 년 10월 03일
//----------------------------------------------------------//

 

이번에 건드려 본 내용은 바로 ToolTip 입니다.

마우스를 가져가면 특정 컴포넌트의 내용이 보여지는 위젯이지요..

스윙에서 제공하는 ToolTipManager는 MouseListener와 MouseMotionListener를 모두 구현하고 있습니다. 따라서 마우스가 특정 컴포넌트 위에 위치하면 그 컴포넌트에 대한 마우스 이벤트가 ToolTipManager에 전달되고 주어진 컴포넌트의 getToolTipLocation() 과 getTooltipText()를 호출함으로써 화면에 그려질 툴팁의 정보를 읽어들이게 됩니다.

JComponenet 에 정의되어 있는 위의 두가지 메소드를 적절히 변형함으로써 툴팁의 출력위치나 내용을 바꾸어 줄 수 있습니다.

1. JDK 의 기본 구현

사용자 삽입 이미지
왼쪽 그림에 보이듯이 마우스가 위치하는 지점을 기준으로 특정 컴포넌트의 toString() 메소드의 내용을 출력하는 것이 기본 구현입니다.

보통 이런 구현도 그럭저럭 쓸만하지만 트리에서는 오히려 화면이 지저분해지는 원인이기도 합니다. 그래서 트리에서 보여지는 툴팁은 마우스가 가리키는 트리 노드에 정확히 위치해서 보이는게 훨씬 더 깔끔하다고 판단하고 작업에 착수하게 되었습니다.

또한 툴팁이란 트리노드의 내용이 화면에 다 보여지지 못하는 경우에만 의미가 있으므로 트리노드의 텍스트의 길이가 현재 JScrollPane의 너비보다 넓지 않으면, 즉 화면에 충분히 보여질 수 있다면 굳이 보여줄 필요가 없다는 점도 고려했습니다. 따라서 구현은 자연스럽게 JTree 를 감싸는 JScrollPane의 JViewport까지 확장됩니다.


2. 첫번째 구현 - 트리노드에 맞추기

첫번째 구현이 완성된 모습입니다.
사용자 삽입 이미지

마우스의 위치가 아니라 마우스가 가리키는 트리노드의 위치를 기준으로 툴팁을 보여주고 있습니다.

tree.getRowForLocation(..) 메소드 호출로 현재 마우스가 가리키는 tree node의 row 를 읽어옵니다. 이 row 를 통해서 현재 마우스가 위치하는 node의TreePath를 얻어올 수 있습니다. getPathBounds(path); 메소드로 TreePath가 가라키는 node의 위치 x,y와 너비 width, height를 Rectangle 객체로 받아오게 됩니다.

하지만 이 Rectangle에서 바로 x,y 좌표를 반환하면 tooltip은 왼쪽에 위치한 아이콘을 가리게 되죠. 이를 막기 위해서 TreeCellRenderer 에서 아이콘의 너비 정보를 더해서 tooltip이 정확히 treenode의 text 부분에 위치하도록 값을 보정해줍니다.(스윙에서 제공하는 기본적인 TreeCellRenderer 구현체는 JLabel을 확장하고 있습니다. 즉, TreeNode 각각은 사실 JLabel 이나 마찬가지입니다.)

이를 나타낸 코드가 아래와 같습니다.

     public Point getToolTipLocation(MouseEvent event)
     {
          if ( getToolTipText() == null ) return super.getToolTipLocation(event);
          Point p = event.getPoint();
          int currentRow = getRowForLocation(p.x, p.y);
          TreePath currentPath = getPathForRow(currentRow);
          TreeCellRenderer r = getCellRenderer();
           Rectangle pathBound = getPathBounds(currentPath);
           if ( pathBound == null )  return super.getToolTipLocation(event);


           // 툴팁을 그리는 렌더러가 JLabel을 상속하므로 이 지점을 기준으로 한다
           JLabel label  = (JLabel)r;
           int width = label.getIcon().getIconWidth();
           pathBound.x += width;
           return pathBound.getLocation();

      }


3. 첫번째 구현의 문제점- 툴팁의 범위가 벗어난 경우

사용자 삽입 이미지
하지만 테스트를 하다보니 왼쪽과 같은 상황이 발생합니다. "Create Stand Alone Application(with SWT, JFace) 라는 툴팁이 보여야할 왼쪽 상단의 위치가 자바 스윙 애플리케이션의 영역을 벗어나 버린겁니다.

빨간색 박스 표시에서 보듯이 스크롤을 해서 트리노드를 이동시키면 트리노드의 좌측상단 좌표가 현재 JScrollPane을 기준으로 음의 값을 갖는게 문제입니다.

화면 캡쳐는 희색으로 나왔지만 툴팁이 계속해서 깜빡거리는 모습입니다. 그러니까 반복해서 계속 툴팁이 그려지고 있다는 것이지요..

왜 이런 현상이 나오는지 자세히 알수는 없었습니다. 툴팁의 구현 내용을 살펴보니 JWindow를 이용하고 있었고 그렇다면 현재 스윙 애플리케이션의 영역과는 아무 상관이 없다고 보아도 될 것 같은데 요상하게 깜빡쇼를 펼치더군요..(아시는 분은 리플 달아주시면 정말 감사할 듯...)

그래서 툴팁의 시작 위치(왼쪽 상단)가 영역을 벗어나면, 그러니까 현재 JScrollPane의 영역을 벗어나는 경우 위치를 맞추는 쪽으려 구현을 변경합니다.

[code] 새로운 구현 - JScrollPane의 스크롤이 변할때마자 이벤트를 통지받기 위해서 ChangetListener를 구현한다.


public class AdvTree extends JTree implements ChangeListener
{
     //tree가 scrollPane의 viewport내에 있을 경우 position 값을 나타낸다.
     int viewX = 0;

     ......


     public Point getToolTipLocation(MouseEvent event)
     {
          if ( getToolTipText() == null ) return null;
          Point p = event.getPoint();
          int currentRow = getRowForLocation(p.x, p.y);
          TreePath currentPath = getPathForRow(currentRow);
          TreeCellRenderer r = getCellRenderer();
          Rectangle pathBound = getPathBounds(currentPath);
          if ( pathBound == null ) return null;
          JLabel label  = (JLabel)r;
          int width = label.getIcon().getIconWidth();
          pathBound.x += width;
   
          // 툴팁의 x좌표값이  viewport에 의해서 가려진 상태이므로
          // viewport의 position에 맞춘다
          if ( pathBound.x < viewX) pathBound.x = viewX;
          return pathBound.getLocation();
     }

        
     // implements javax.swing.event.ChangeListener
     public void stateChanged(ChangeEvent e)

     {
          Object source = e.getSource();
          if (source instanceof JViewport){
               JViewport view = (JViewport) e.getSource();
               viewX = view.getViewPosition().x;
          }
     }

}

.....

// registering changet listener for viewport scrolling

scrollpane.getViewport().addChangetListener((ChangetListener)tree);


4. 결과


사용자 삽입 이미지


이제 스크롤에 상관없이 영역이 벗어나면 툴팁이 자동으로 왼쪽 가장자리로 이동하게 됩니다.

'Dev > Java' 카테고리의 다른 글

[java.awt.Container] Container 에 Component 를 추가, 삭제  (0) 2007.12.12
Java Wizard Dialog  (0) 2007.12.08
Java System Properties  (0) 2007.12.08
Posted by yeori
,