IT_Programming/Java

[펌] Java AWT:Lightweight UI Framework

JJun ™ 2010. 10. 8. 13:51

------------------------------------------------------------------------------------------------

출처: http://pllab.kw.ac.kr/j2seAPIs/guide/awt/1.3/designspec/lightweights.html

------------------------------------------------------------------------------------------------ 

 

 

최종 갱신일:1997 연 1 월 31 일

문제

1.0 AWT 에 관한 문제의 1 개는, 새로운 컴퍼넌트를 작성하려면 java.awt.Canvas 또는 java.awt.Panel 의

서브 클래스를 작성할 필요가 있는 것으로, 이것은, 새로운 컴퍼넌트가 각각 자신의 불투명한 네이티브

윈도우를 소유하는 것을 의미합니다. 이 컴퍼넌트와 네이티브 윈도우간의 1 대 1 대응으로부터,

결과적으로 다음의 3 개의 문제가 생깁니다.

  1. 네이티브 윈도우는 중량이 있기 (위해)때문에, 너무 많은 윈도우를 가지는 것이 바람직하지 않다
  2. 네이티브 윈도우가 불투명해, 투명한 영역을 구현하기 위해서 사용할 수 없다
  3. 네이티브 윈도우는 플랫폼에 의해 처리가 달라, AWT 가 이것들 다른 플랫폼에서 일관한 뷰를 유지하는 것은, 꽤 곤란하다

「경량」UI 컴퍼넌트의 작성을 가능하게 하는 훅이 구현되었습니다. 이러한 훅은 Lightweight UI Framework 로 불립니다.

Lightweight UI Framework

Lightweight UI Framework 는 매우 단순합니다. 이것은 요컨데, 관련한 네이티브 불투명 윈도우를 가지지

않는 컴퍼넌트를 작성하기 위해서,java.awt.Componentjava.awt.Container 클래스를 직접 확장하는

기능이라고 할 수 있습니다. 이러한 경량 컴퍼넌트와 컨테이너는, painting, 레이아웃, 이벤트등의 기존의 AWT 모델과 잘 적합해, 그 때문에 특별한 처리나 API 의 추가를 필요로 하지 않습니다.

Canvas 나 Panel 의 기존의 서브 클래스는, 그 슈퍼 클래스를 적절히 변경하는 것만으로, 경량 버젼에

간단하게 이행 할 수 있습니다. 경량 컴퍼넌트를 작성하는 이점은, 다음과 같습니다.

  • 지금은, 「경량」컴퍼넌트는, paint() 메소드로 이러한 영역에 렌더링 하지 않는 것뿐으로, 투명한 영역을 가질 수가 있다 (다만 Java2D 의 전면적인 서포트를 얻을 때까지는, 바운딘그복스는 구형인 채이다)
  • 「경량」컴퍼넌트는, 네이티브 데이터 구조 또는 피어 클래스를 필요로 하지 않는다고 하는 점으로써, 가볍다
  • 경량 컴퍼넌트 native code는 필요없고, 그 처리는 일반적인 Java 코드에 의해 100% 구현되어 플랫폼에 의존하지 않는다

다음의 툴 킷의 버젼 (1.1 이상)으로 이 체제를 사용해, 기본 UI 컨트롤 (Button, List 등)의 순수한 Java 버젼을 구현하려고 하고 있습니다. 이 컨트롤은 플랫폼을 넘어, 공통의 Look & Feel 를 구현합니다 (네이티브 피어를 사용하지 않는다).

경량과 중량 컴퍼넌트의 혼재

경량 컴퍼넌트는, 기존의 중량 컴퍼넌트와 자유롭게 혼재할 수 있습니다. 이것은, 경량 컴퍼넌트를 중량 컨테이너의 직접적인 아이로 해, 중량 컴퍼넌트를 경량 컨테이너의 직접적인 아이로 해, 그리고 중량과 경량을

컨테이너내에 혼재시킬 수가 있다고 하는 것입니다. 지정한 순서에 관계없이, 경량과 중복 하는 경우, 중량이 항상 「상부」가 되는 것에 주의해 주세요.

기존의 패널에의 경량 컴퍼넌트의 배치

경량 컴퍼넌트의 painting와 이벤트 배포 기구는, Container 클래스에 의해 처리됩니다. 이것은, 경량 컴퍼넌트의 painting가 Container 의 paint() 메소드내로부터 방아쇠 되는 것을 의미합니다. 그 때문에, 경량 컴퍼넌트의 paint 메소드가 오버라이드(override) 되지만, super.paint()를 호출하지 않는 인스턴스의 내부에 놓여졌을 경우, 경량 컴퍼넌트의 paint() 메소드는 결코 불려 가지 않습니다. 이것은, 경계 또는 경사면의 painting를 구현하기 위해서 Panel 를 확장하지만, super.paint()를 호출하지 않는 기존의 클래스를 사용하는 경우, 자주(잘) 일어납니다 (1.0. 2 에 관한 문제는 아니기 때문에). 경량 컴퍼넌트가 표시되지 않는 경우, 최초로 이 점을 확인해 주세요.

더블 버퍼링

경량 컴퍼넌트는 전체적으로 Java 로 렌더링 되기 (위해)때문에, Container 내에서 더블 버퍼링을 사용하는 곳에 의해, 깜박거림을 피해 렌더링이 매우 원활히 됩니다. 디폴트에서는, Container 클래스는 더블 버퍼링을 구현합니다만, 간단하게 구현할 수 있습니다. 내부에 놓여진 경량 컴퍼넌트 모두에게, 매끄러운 렌더링을

구현하는 더블 버퍼의 Panel 의 예를 다음에 나타냅니다.


public class DoubleBufferPanel extends Panel {    
  Image offscreen;
  /**
   * null out the offscreen buffer as part of invalidation
   */
  public void invalidate() {
      super.invalidate();
      offscreen = null;
  }
  /**
   * override update to *not* erase the background before painting
   */
  public void update(Graphics g) {
      paint(g);
  }
  /**
   * paint children into an offscreen buffer, then blast entire image
   * at once.
   */
  public void paint(Graphics g) {
      if(offscreen == null) {
         offscreen = createImage(getSize(). width, getSize(). height);
      }
      Graphics og = offscreen.getGraphics();
      og.setClip(0,0, getSize(). width, getSize(). height);
      super.paint(og);
      g.drawImage(offscreen, 0, 0, null);
      og.dispose();
  }
}

 

 


샘플 코드

경량의 원형 버튼 클래스의 작성을 나타내는 샘플 코드를 다음에 나타냅니다.

이것은, 경량 컴퍼넌트의 투명함을 두드러지게 합니다.


import java.lang. *;
import java.util. *;
import java.awt. *;
import java.awt.event. *;
/**
 * RoundButton - a class that produces a lightweight button.
 */
public class RoundButton extends Component {
  String label;                      // The Button's text
  protected boolean pressed = false; // true if the button is detented.
  
  /**
   * Constructs a RoundButton with the specified label.
   * @param label the label of the button
   */
  public RoundButton(String label) {
      this.label = label;
      enableEvents(AWTEvent.MOUSE_EVENT_MASK);
  }
  /**
   * paints the RoundButton
   */
  public void paint(Graphics g) {
      int s = Math.min(getSize(). width - 1, getSize(). height - 1);
      // paint the interior of the button
      if(pressed) {
          g.setColor(getBackground(). darker(). darker());
      } else {
          g.setColor(getBackground());
      }
      g.fillArc(0, 0, s, s, 0, 360);
      // draw the perimeter of the button
      g.setColor(getBackground(). darker(). darker(). darker());
      g.drawArc(0, 0, s, s, 0, 360);
      // draw the label centered in the button
      Font f = getFont();
      if(f ! = null) {
          FontMetrics fm = getFontMetrics(getFont());
          g.setColor(getForeground());
          g.drawString(label,
                       s/2 - fm.stringWidth(label)/2,
                       s/2 + fm.getMaxDescent());
      }
  }
  /**
   * The preferred size of the button.  
   */
  public Dimension getPreferredSize() {
      Font f = getFont();
      if(f ! = null) {
          FontMetrics fm = getFontMetrics(getFont());
          int max = Math.max(fm.stringWidth(label) + 40, fm.getHeight() + 40);
          return new Dimension(max, max);
      } else {
          return new Dimension(100, 100);
      }
  }
  /**
   * The minimum size of the button.  
   */
  public Dimension getMinimumSize() {
      return new Dimension(100, 100);
  }
   /**
    * Paints the button and distribute an action event to all listeners.
    */
   public void processMouseEvent(MouseEvent e) {
       Graphics g;
       switch(e.getID()) {
          case MouseEvent.MOUSE_PRESSED:
            // render myself inverted....
            pressed = true;
            // Repaint might flicker a bit.  To avoid this, you can use
            // double buffering (see the Gauge example).
            repaint(); 
            break;
          case MouseEvent.MOUSE_RELEASED:
            // render myself normal again
            if(pressed == true) {
                pressed = false;
                // Repaint might flicker a bit.  To avoid this, you can use
                // double buffering (see the Gauge example).
                repaint();
            }
            break;
          case MouseEvent.MOUSE_ENTERED:
            break;
          case MouseEvent.MOUSE_EXITED:
            if(pressed == true) {
                // Cancel!  Don't send action event.
                pressed = false;
                // Repaint might flicker a bit.  To avoid this, you can use
                // double buffering (see the DoubleBufferPanel example above).
                repaint();
                // Note: for a more complete button implementation,
                // you wouldn't want to cancel at this point, but
                // rather detect when the mouse re-entered, and
                // re-highlight the button.  There are a few state
                // issues that that you need to handle, which we leave
                // this an an excercise for the reader (I always
                // wanted to say that! )
            }
            break;
       }
       super.processMouseEvent(e);
   }   
}

 



코멘트의 송부처: java-awt@java.sun.com

Copyright © 1997, Sun Microsystems, Inc. All rights reserved.