Abstract Window Toolkit (AWT) は、Merlinの新規リリースによってアップグレードされました。変更点の多くは小規模なものですが、解説しておくだけの重要性はあります。今月は次の4つの新機能に注目しましょう。
- 画面アクセサリーが使用するスペースを検出する機能
-
Colorクラスにおける定数の大文字化 - マウス・ホイールのイベントへの反応をサポート
- マウス・ボタン、キーパッド・キー、シフト・キーをより適切に識別する機能
AWTの新機能を説明するために、画面のサイズをフル・サイズ(からツールバーのようなデスクトップ・アクセサリーを引いた部分)まで拡大し、新しいカラー定数を使用して背景を設定するプログラムを作成します。このプログラムでは、マウス・ホイールを回すか、または左右のシフト・キーを使用して、背景色を変更することができます。
リスト1では、プログラムの基本的な外枠を紹介します。残念ながら、利用するマウス・ホイールのサポート機能にはまだバグがあります(詳細については参考文献を参照)。これを機能させるために、ここではJButton をJScrollPane のフレームに追加しています。このバグのため、この外枠は必要以上に複雑になっています。
リスト1. クローズ可能なフレームを作成するシェル
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AwtTest {
// See bug 4475240
static JButton button = new JButton();
static JScrollPane pane = new JScrollPane(button);
public static void main(String args[]) {
JFrame frame = new JFrame("AwtTest");
// See bug 4475240
frame.getContentPane().add(button, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}
|
以前のコラムでは、
setExtendedState() を使ってFrame を最大化する機能について述べました。状態をFrame.MAXIMIZED_BOTH に設定すれば、フレームは最大化されます。ただし、フレームを最大化したくはないが、デスクトップ・アクセサリーを除いたユーザーのデスクトップ領域いっぱいにサイズを変更したい場合は、アクセサリーが使用するInsetsが必要です(リスト2を参照)。この情報については、ツールキットのgetScreenInsets() メソッドによって提供されます。画面サイズからインセットを差し引くと、ウィンドウはあるべきサイズになります。適切に配置するには、上部インセットと左インセットも使用する必要があります。
リスト2. 画面のサイズ変更
private static void sizeScreen(JFrame frame) {
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
GraphicsConfiguration config = frame.getGraphicsConfiguration();
Insets insets = kit.getScreenInsets(config);
screenSize.width -= (insets.left + insets.right);
screenSize.height -= (insets.top + insets.bottom);
frame.setSize(screenSize);
frame.setLocation(insets.left, insets.top);
}
|
main() メソッドにsizeScreen() の呼び出しを追加してください。
Java言語仕様のセクション6.8には、パッケージ、メソッド、フィールド、定数などのアイテムの命名規則が記載されています。定数名に関するサブセクション (6.8.5) では、定数に小文字を使ってはならないと記載されていますが、red、green、blue のようなColor クラスのカラー定数はすべて最初から全文字が小文字で指定されています。Merlinではレコードを訂正して、同じ定数をすべて大文字でも提供するようになりました。まだ前の名前を使えないようにしていませんが、Java Standard Edition 1.4プログラムでは新規の名前を使用することができます。
このテスト・プログラムでは、カラー定数の配列とそれらをループするカウンターを作成し、2つの方向のうちいずれかにその配列をループするchangeBackground() メソッドを用意します(リスト3を参照)。
リスト3. 背景色の変更
private static final Color colors[] = {
Color.BLACK,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.RED,
Color.WHITE,
Color.YELLOW
};
static int colorCounter;
private static final int UP = 1;
private static final int DOWN = 2;
private static void changeBackground(JFrame frame, int direction) {
// See bug 4475240
// w/o bug, change background of getContentPane()
button.setBackground(colors[colorCounter]);
// Update counter based on direction
if (direction == UP) {
colorCounter++;
} else {
--colorCounter;
}
// Wrap colors
if (colorCounter == colors.length) {
colorCounter = 0;
} else if (colorCounter < 0) {
colorCounter = colors.length-1;
}
}
|
main() ルーチンにchangeBackground() メソッドへの呼び出しを追加してください。
命名規則はGridBagLayout などのクラスでも更新されました。そのようなクラスには、AdjustForGravity() のようなプロテクトされたメソッドが、小文字から始まるように変更されています。
Java開発者が前世紀から強く求めていた1つの機能は、マウス・ホイールの回転のサポートです。Merlinのリリースにより、MouseWheelListener をどんなComponent にも付加して、うまく反応できるようになりました。先に述べたバグを別にすれば、これは問題なく機能します。JScrollPane コンポーネントには事前登録済みのリスナーも付属しています。そのため、フォーカスがあるときにユーザーがホイールを動かすと、ペインがスクロールします。
リスナーには、その唯一つのメソッドとしてmouseWheelMoved() があり、これはMouseWheelEvent を引数にとります。このイベントから、getScrollType() がユニット・スクロールであろうとブロック・スクロールであろうとgetScrollAmount() によってスクロール量を、getWheelRotation によってホイール回転の方向と数を、そして便宜的なものとしてgetUnitsToScroll() によってスクロール単位を確認することができます。
このテスト・プログラムでは、リスト4で示すような方向に基づいて背景色にカラー配列をループさせるホイール・リスナーを付加します。ドキュメンテーションには、マウス・ホイールのイベントはコンテナーの階層をさかのぼると記載されていますが、バグがあるためこのようにならないので、ボタンにリスナーを付加する必要があります。このリスナーを必ずmain() メソッドで付加してください。
リスト4. ホイール・リスナーの付加
private static void attachMouseWheelListener(final JFrame frame) {
MouseWheelListener listener = new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {
int count = e.getWheelRotation();
int direction = (Math.abs(count) > 0) ? UP : DOWN;
changeBackground(frame, direction);
}
};
button.addMouseWheelListener(listener);
}
|
このテスト・プログラムでは付加された機能のほんの一部しか利用しないのですが、この最後の機能は、当初の想像よりも広範囲にわたります。基本的に、Merlinは 同じキー (キーパッドおよび標準) の様々なバージョンと、キーボード・キーおよびマウス・ボタンの様々なシーケンスを識別する機能を付加します。たとえば以前には、シフト・クリックと、異なるボタンのクリックとの違いを伝える方法がありませんでした。両方とも同じマウス・イベントを引き起こしていました。InputEvent クラスで次の新規の定数を用いると、すべてが変わります。
-
ALT_DOWN_MASK -
CTRL_DOWN_MASK -
META_DOWN_MASK -
SHIFT_DOWN_MASK -
BUTTON1_DOWN_MASK -
BUTTON2_DOWN_MASK -
BUTTON3_DOWN_MASK -
BUTTON1_CHANGED_MASK -
BUTTON2_CHANGED_MASK -
BUTTON3_CHANGED_MASK
どのボタンを押しているのかだけを調べたい場合は、次のメソッドがより役立ちます。
-
isButton1Down() -
isButton2Down() -
isButton3Down()
MouseEvent は、状態を変更したボタンを発見するgetButton() メソッドも提供します。
こうしたすべてのメソッドと定数によって、ボタン・イベントへの反応はより容易になるはずです。
同じキーの様々なバージョンを識別するには、KeyEvent クラスの変更が必要です。getKeyLocation() によってそのイベントにキー配置を求めることができます。次の定数のうちいずれかを戻します。
-
KEY_LOCATION_LEFT -
KEY_LOCATION_NUMPAD -
KEY_LOCATION_RIGHT -
KEY_LOCATION_STANDARD -
KEY_LOCATION_UNKNOWN
キーの配置場所を求めることによって、キーパッドと標準キー、および左右のシフト・キーを識別することができます。
このテスト・プログラムでは、リスト5で示すように、左と右のどちらのシフト・キーが押されているかに基づいて、キー・リスナーにループ・アップやループ・ダウンを実行させるだけです。必ず最初に、押されているキーをチェックし、それから配置をチェックする必要があります。そしてもちろん、ルーチンへの呼び出しはmain() メソッドに追加します。
リスト5. キー・リスナーの付加
private static void attachKeyListener(final JFrame frame) {
KeyListener listener = new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
int location = e.getKeyLocation();
int direction = (location == KeyEvent.KEY_LOCATION_LEFT) ? UP : DOWN;
changeBackground(frame, direction);
}
}
};
button.addKeyListener(listener);
}
|
リスト6では、以上の新機能を試用するための完全な例を紹介します。マウス・ホイール・リスナーのバグが修正されると、ボタンを使用しなくても、ペインをスクロールすることができます。
リスト6. 完全な例
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AwtTest {
private static final Color colors[] = {
Color.BLACK,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.RED,
Color.WHITE,
Color.YELLOW
};
static int colorCounter;
private static final int UP = 1;
private static final int DOWN = 2;
// See bug 4475240
static JButton button = new JButton();
static JScrollPane pane = new JScrollPane(button);
private static void sizeScreen(JFrame frame) {
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
GraphicsConfiguration config = frame.getGraphicsConfiguration();
Insets insets = kit.getScreenInsets(config);
screenSize.width -= (insets.left + insets.right);
screenSize.height -= (insets.top + insets.bottom);
frame.setSize(screenSize);
frame.setLocation(insets.left, insets.top);
}
private static void changeBackground(JFrame frame, int direction) {
// See bug 4475240
// w/o bug, change background of getContentPane()
button.setBackground(colors[colorCounter]);
// Update counter based on direction
if (direction == UP) {
colorCounter++;
} else {
--colorCounter;
}
// Wrap colors
if (colorCounter == colors.length) {
colorCounter = 0;
} else if (colorCounter < 0) {
colorCounter = colors.length-1;
}
}
private static void attachMouseWheelListener(final JFrame frame) {
MouseWheelListener listener = new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {
int count = e.getWheelRotation();
int direction = (Math.abs(count) > 0) ? UP : DOWN;
changeBackground(frame, direction);
}
};
// See bug 4475240
// w/o bug, add to frame
button.addMouseWheelListener(listener);
}
private static void attachKeyListener(final JFrame frame) {
KeyListener listener = new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
int location = e.getKeyLocation();
int direction = (location == KeyEvent.KEY_LOCATION_LEFT) ? UP : DOWN;
changeBackground(frame, direction);
}
}
};
// See bug 4475240
// w/o bug, add to frame
button.addKeyListener(listener);
}
public static void main(String args[]) {
JFrame frame = new JFrame("AwtTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// See bug 4475240
frame.getContentPane().add(button, BorderLayout.CENTER);
sizeScreen(frame);
changeBackground(frame, UP);
attachMouseWheelListener(frame);
attachKeyListener(frame);
frame.show();
}
}
|
-
Java言語仕様のセクション6.8には、以前に一部のシステム・クラスで無視されていた命名規則が定義されています。
-
Bug Report 4475240 で
MouseWheelListenerバグについてお読みください。(ご覧頂くにはIDの登録が必要です。) -
Bug Report 4289845 でマウス・ホイールのサポート要求についてお読みください。(ご覧頂くにはIDの登録が必要です。)
- IBM
Accessibility Center(日本語サイト) では、身体障害者用のユーザー・インターフェースを作成する際の課題に取り組んでいます。
- Bertrand Portierは、自身の記事「Java 2が新規フォーカス・サブシステムを実装」(developerWorks、2001年10月)で、新しい(大幅に改良された) AWTフォーカス・モデルについて解説し、現在のコードを新規モデルにマイグレーションするヒントを紹介しています。
- " Java 2 user interface" (developerWorks、2001年7月) で、どのようにJava UIが進化してきたかについてお読みください。
- John Zukowskiによる、以下の「Merlinの魔術」シリーズもご一読ください。
- Swingの新たなSpinnerコンポーネント: JSpinner を使用して、ユーザーがピック・リストから素早く日付、数値および選択肢を選べるようにする
- もう1つのシンプルなフレーム: AWT Frameを最大化し、その装飾を取り外す
- 長期の永続性: JavaBeanコンポーネントの状態をXMLにシリアライズする
- 挿入順序を保持する: 新たにリンクされたHashSetとHashMapの実装を使ってみる
- タブ付きペインのスクロール: 大き過ぎて収まりきらない場合にどうするか
- Porter-Duffのルール!: Java 2Dが残りの4つのルールを追加
- プリファレンスを使ってみる: JSR 10、Preferences API Specificationによりプリファレンス・データと構成データの操作が可能に
-
developerWorks Javaテクノロジー・ゾーン で他のJava参考文献もお読みください。

John Zukowskiは、JZ Ventures, Inc.の戦略的Javaコンサルティングを推進し、数多くのjGuruのコミュニティー主導のJava FAQsで常任指導者として活躍しています。最新の著書には、Apressから出版された「 Java Collections」および「 Definitive Guide to Swing for Java 2」 (第2版) があります。彼のメール・アドレスはjaz@zukowski.net です。