Duke logo doing a cartwheel Java AWT:Lightweight UI Framework


最終更新日: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.Component および java.awt.Container クラスを直接拡張する機能といえます。これらの軽量コンポーネントとコンテナは、ペインティング、レイアウト、イベントなどの既存の AWT モデルとうまく適合し、そのため特別な処理や API の追加を必要としません。Canvas や Panel の既存のサブクラスは、そのスーパークラスを適切に変更するだけで、軽量バージョンに簡単に移行できます。

軽量コンポーネントを作成する利点は、次のとおりです。

次のツールキットのバージョン (1.1 以上) でこのフレームワークを使用し、基本 UI コントロール (Button、List など) の純粋な Java バージョンを実装しようとしています。 このコントロールはプラットフォームを越えて、共通の Look & Feel を実装します (ネイティブピアを使用しない)。

軽量と重量コンポーネントの混在

軽量コンポーネントは、既存の重量コンポーネントと自由に混在できます。このことは、軽量コンポーネントを重量コンテナの直接の子にし、重量コンポーネントを軽量コンテナの直接の子にし、そして重量と軽量をコンテナ内に混在させることができるということです。 指定した順序にかかわらず、軽量と重複する場合、重量の方が常に「上部」になることに注意してください。

既存のパネルへの軽量コンポーネントの配置

軽量コンポーネントのペインティングとイベント配布機構は、Container クラスによって処理されます。これは、軽量コンポーネントのペインティングが Container の paint() メソッド内からトリガされることを意味します。そのため、軽量コンポーネントの paint メソッドがオーバーライドされるが、super.paint() を呼び出さないインスタンスの内部に置かれた場合、軽量コンポーネントの paint() メソッドは決して呼び出されません。これは、境界または斜面のペインティングを実装するために 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.