Home >> Lecture 10
アプレット(および、フレームやパネル)などのコンテナ(Container)クラスのオブジェクトにおいて、 キー・マウスのイベントに対応するのは、ボタンを使った方法と同じで、 リスナー系のインターフェースを用います。 各リスナーに対応して、以下の表に示されるようなメソッドを、クラスの中に定義する必要があります。
リスナー | リスナーの内容 | 実装するメソッド | 内容 |
---|---|---|---|
KeyListener | キー入力に対処する | ||
keyTyped( KeyEvent e ) | キーが1回タイプされたときに呼び出される | ||
keyPressed( KeyEvent e ) | キーが押されたときに呼び出される | ||
keyReleased( KeyEvent e ) | キーが離されたときに呼び出される | ||
MouseListener | マウス入力に対処する | ||
mouseClicked( MouseEvent e ) | マウスボタンがクリックされたときに呼び出される | ||
mousePressed( MouseEvent e ) | マウスボタンが押されたときに呼び出される | ||
mouseReleased( MouseEvent e ) | マウスボタンが離されたときに呼び出される | ||
mouseEntered( MouseEvent e ) | マウスポインタが描画領域に入ったとき | ||
mouseExited( MouseEvent e ) | マウスポインタが描画領域から出たとき | ||
MouseMotionListener | マウスの動きに対処する | ||
mouseDragged( MouseEvent e ) | マウスボタンがドラッグされたときに呼び出される | ||
mouseMoved( MouseEvent e ) | マウスポインタが動いたときに呼び出される |
これらのリスナー系のインターフェースを使った場合は、 必要のないメソッドまで定義しておかなければならないのですが、 その分どのようなイベントが発生したときにどのメソッドが呼び出されるかの対応がわかりやすくなっています。
キー入力に対応するには、クラスの定義のところで、KeyListenerインターフェースを用います。 次にアプレットがキー入力時に発生するイベントに対応することを登録する必要があります。 この登録は、initメソッドの中でaddKeyListenerメソッドを使って行ないます。 これは、ボタンのときに、addActionListenerを使って登録したのと同様です。 ただし、今回は対象となるコンポーネントがアプレット自身なので、 addKeyListenerメソッドの呼出しは対象を指定せず、以下のように直接呼び出します。
addKeyListener( this ); // アプレット上のキー入力をアプレット自身が対応するこのメソッドは、スーパークラスのAppletクラスで実装されていますので、repaintメソッドと同じようにオブジェクト名を省略して呼び出すことができます。以下のように、thisやsuper(この場合addKeyListenerがスーパークラスから継承されたメソッドなので)をつけて呼び出しても構いません。
this.addKeyListener( this );
KeyListenerインターフェースを利用する際に定義しなければならない次の3つのメソッドは、ユーザがキー入力が行なってイベントが発生したときに呼び出されるメソッドになっています。
▼キーイベントに対処するメソッドの書式
public void keyTyped( KeyEvent e ) {
//キーがタイプされたときに行なうこと
}
public void keyPressed( KeyEvent e ) {
// キーが押されたときに行なうこと
}
public void keyReleased( KeyEvent e ) {
// キーが離されたときに行なうこと
}
キー入力では、発生するキーイベントとしては、次の3つあります。 この3つのイベントに対応して、それぞれのメソッドが呼ばれることになります。
タイミング | イベント名 | 呼び出されるメソッド | |
---|---|---|---|
キーが打たれた瞬間 | KEY_PRESSED | → | keyPressed |
キーが離された瞬間 | KEY_RELEASED | → | keyReleased |
一連のキー入力が実行されたとき | KEY_TYPED | → | keyTyped |
1つのキーが押されて、離されるまでに、これら3つのイベントが発生します。 たとえば、小文字のmというキーをユーザが入力する際には、上の順番で3つのイベントが発生します。 KEY_TYPEDは、離された後(KEY_RELEASEDの後)に発生することに注意してください。
keyTypedとkeyPressedの違いは、大文字のAをキーボードで打つ場合には明確になります。 大文字の「A」を打つためには、「SHIFTキー」を予め押さなければなりません。 「SHIFTキー」を押した段階でkeyPressedメソッドが呼び出されます。 そして、小文字の「a」を押した段階でも再びkeyPressedメソッドが呼び出されます。 そして、「a」を離した段階でkeyReleasedメソッドが呼び出されます。 その後初めて、keyTypedメソッドが呼び出され、そこでは大文字の「A」が押されたことが報告されます。
イベントの種類に対応するために、必要以外のメソッドは、何もしないように記述しておきます。 たとえば、次の記述では、タイプされたときだけ何かを行ない、 それ以外のとき(押された段階や離された段階で)は、何もしないように記述しています。
public void keyTyped( KeyEvent e ) { キーがタイプされたときに行ないたいこと } public void keyPressed( KeyEvent e ) { } // 何もしない public void keyReleased( KeyEvent e ) { } // 何もしない
キーがタイプされたの場合は、キーイベントに対して、getKeyChar( )メソッドを呼び出してやれば、 どのキーが押されたか文字として得ることができます。
char c = e.getKeyChar( );
たとえば、ユーザのキー入力を得るためには、次のようにkeyTypedメソッドを記述します。 これは、アプレット上で入力されたキーをステータスバーに表示しています。
public void keyTyped( KeyEvent e ) { char c = e.getKeyChar( ); showStatus( "Input Character: " + c ); }
※Webブラウザによっては、アプレットをアクティベート(顕在化)させないと、 アプレットにキー入力がいかない場合があります。 そのときは、マウスで一回アプレットが描画している領域のどこかをクリックしてください。 あるいは、アプレットビューワなどでは、TABキーを押せばキー入力がアプレットに移ることがあります。
keyTypedメソッドでは、キーの周りについている特殊なキーの場合には、文字としては得ることができません。 keyPressedやkeyReleasedメソッドの場合は、特殊なキーの入力を知ることができます。 キーイベントに対して、getKeyCodeメソッドを呼び出します。 このメソッドは、押されたキーに対応したシンボルコード(キーコード)を返してきます。 キーコードは、整数型であることに注意してください。
int keycode = e.getKeyCode( );
キーコードについては、KeyEventクラスに次のようなシンボルで定義されています。 代表的なものをいくつかここで挙げましょう。 すべてのキーに対応して定義されていますので、Java SEのWebマニュアルを参照してください。
定義値 | キーの種類 |
---|---|
KeyEvent.VK_HOME | Homeキー |
KeyEvent.VK_END | Endキー |
KeyEvent.VK_PAGE_UP | Page Upキー |
KeyEvent.VK_PAGE_DOWN | Page Downキー |
KeyEvent.VK_UP | ↑キー |
KeyEvent.VK_DOWN | ↓キー |
KeyEvent.VK_LEFT | ←キー |
KeyEvent.VK_RIGHT | →キー |
KeyEvent.VK_CONTROL | Controlキー |
KeyEvent.VK_SHIFT | Shiftキー |
KeyEvent.VK_ALT | Alt(あるいはOption)キー |
KeyEvent.VK_META | Meta(あるいはCommand)キー |
KeyEvent.VK_WINDOWS | Windowsキー |
なお、Return(Enter)キーや、Backspaceキー、あるいはTABキーやEscapeキーなどは、 Java言語で文字として定義してありますので、getKeyCharメソッドで文字として処理することも可能です。 たとえば、Homeキーが押されたのを判別して何か処理をさせるためには、 一般的には次のようにkeyPressedメソッドの方を用いて、その中でif文を用います。
public void keyPressed( KeyEvent e ) { int keycode = e.getKeyCode( ); if ( keycode == KeyEvent.VK_HOME ) { // ホームキーが押されたときにすること } }
キー入力のようなのイベントの処理の場合でも、 MVCプログラミングとして、同じようにアプレットのプログラムを設計します。 インスタンス変数がモデルを示し、paintメソッドはビューを、 そしてkeyTypedメソッドはコントロールを表しています。
- インスタンス変数にモデルとなる情報を格納しています
- paintメソッドでインスタンス変数の値を元に何かの表示を行ないます
- keyTyped(あるいはkeyPressed, keyReleased)メソッドでユーザの入力を得て、インスタンス変数の値を変えます
通常の文字をただ単に画面に表示するためのアプレットを作ってみます。 インスタンス変数 c がモデルになっています。 paintメソッドでは、それを画面に表示しています。 keyTypedでは、ユーザのキー入力をインスタンス変数に代入しています。
import java.awt.*; import java.awt.event.*; import java.applet.*; public class KeyIn extends Applet implements KeyListener { char c; // キー入力された文字を保持する public void init( ) { addKeyListener( this ); // キー入力にアプレット自身が対応する c = 'A'; // 仮に最初はAとする。 } public void paint( Graphics g ) { g.drawString( "Key In: " + c, 100, 100 ); } public void keyTyped( KeyEvent e ) { c = e.getKeyChar( ); // 打たれた文字を取り出す repaint( ); // 再描画させる } public void keyPressed( KeyEvent e ) { } // 押されたときは何もしない public void keyReleased( KeyEvent e ) { } // 離されたときも何もしない }
ボタンなどのコンポーネントと組み合わせる場合は、コンポーネントがキー入力を受け付けてしまいますので、 ボタンを押してしまうと、キー入力の対象がボタンに移ってしまいますので、 以降アプレットでキー入力が受け付けられなくなります。 そこで、引き続きアプレットでキー入力を受け付けたい場合は、 requestFocusという名前のメソッドを呼び出して、キー入力の対象をアプレット自身に戻してやる必要があります。
以下のアプレットは、文字の入力を行なうアプレットですが、 ボタンが押される度に、フォントサイズを大きくしています。 actionPerformedメソッドと、initメソッドでは、 アプレット自身がキー入力を受け取るための指定をするために、requestFocusメソッドを呼び出しています。
import java.awt.*; import java.awt.event.*; import java.applet.*; public class ExtendedKeyIn extends Applet implements KeyListener, ActionListener { Button button; int size = 10; char c = 'A'; int code=61; public void init( ) { button = new Button( "Expand" ); button.addActionListener( this ); add( button ); addKeyListener( this ); requestFocus( ); } public void paint( Graphics g ) { g.setFont( new Font( "Serif", Font.PLAIN, size ) ); g.drawString( "Key Typed: " + c, 50, 150 ); g.drawString( "Key Code: " + code, 50, 170+size ); } public void actionPerformed( ActionEvent e ) { size = ( size - 10 + 2 ) % 100 + 10; // 10ポイント〜110ポイントの間で、 repaint( ); // 2ポイントずつ大きくしていく requestFocus( ); // 再びアプレット自身がキー入力を貰う対象とする } public void keyTyped( KeyEvent e ) { c = e.getKeyChar( ); // 打たれた文字を取り出す repaint( ); // 再描画させる } public void keyPressed( KeyEvent e ) { code = e.getKeyCode( ); repaint( ); } public void keyReleased( KeyEvent e ) { } }
マウス入力に対応するには、クラスの定義のところで、MouseListenerインターフェースを用います。また、アプレットがマウスの入力に対応することを登録する必要があります。initメソッドの中でaddMouseListenerメソッドを呼び出して、登録を行ないます。このメソッドを呼び出すときも、コンポーネントはアプレット自身ですし、またアプレット自身がマウスの入力に対応しますので、次のように記述します。
addMouseListener( this ); // マウスの入力に対してアプレット自身が対応する
this.addMouseListener( this ); と記述してもいいのですが、アプレット自身に対してはthis.を省略できますので、上のように記述して構いません。このaddMouseListenerは、Componentクラスのメソッドなのですが、AppletクラスはComponentクラスを継承していますし、また皆さんが定義するアプレットは、Appletクラスを継承していますので、このメソッドを利用可能になっています。
MouseListenerに対応して、マウスイベントが発生したときに呼び出されるメソッドは次の5つのものを記述しなければなりません。
▼マウス入力に対応するメソッドの書式
public void mouseClicked( MouseEvent e ) {
// マウスがクリックされたときに行なうこと
}
public void mousePressed( MouseEvent e ) {
// マウスボタンが最初に押されたときに行なうこと
}
public void mouseReleased( MouseEvent e ) {
// マウスボタンが離されたときに行なうこと
}
public void mouseEntered( MouseEvent e ) {
// マウスが描画領域に入ったときに行なうこと
}
public void mouseExited( MouseEvent e ) {
// マウスが描画領域から出たときに行なうこと
}
キー入力と同じように、マウスに対して行なわれるさまざまな動作に対応して、イベントが発生します。たとえばマウスがクリックされたときだけ処理を行ないたい場合は次のmouseClickedメソッドを定義します。
public void mouseClicked( MouseEvent e ) { // マウスがクリックされたときに行なうこと }
あるいは、続けてドラッグ操作にはいるようなときは、ユーザはそのままボタンを押し続けることになります。クリック操作は、ボタンが押されて離されることを前提としています。そのような場合に、最初にマウスが押された場所を把握したければ、次のmousePressedメソッドを定義します。
public void mousePressed( MouseEvent e ) { // マウスボタンが最初に押されたときに行なうこと }
キー入力のときに発生するイベントと同様に、マウスボタンが押されて離される場合には、押されたときにMOUSE_PRESSEDイベントが発生し、離されるときMOUSE_RELEASEDイベントが発生し、その直後に一回クリックされたことを示すMOUSE_CLICKEDイベントが発生します。それぞれのイベントに対応してmousePressed、mouseReleased、およびmouseClickedメソッドが呼ばれることになります。
マウス入力の場合もキー入力と同様で対処する必要のないイベントに対しては、何もしないように記述します。たとえば、以下の記述は、マウスのクリックだけに対処するような記述の仕方になっています。
public void mouseClicked( MouseEvent e ) { マウスがクリックされたときに行なうこと } public void mousePressed( MouseEvent e ) { } // 何もしない public void mouseReleased( MouseEvent e ) { } // 何もしない public void mouseEntered( MouseEvent e ) { } // 何もしない public void mouseExited( MouseEvent e ) { } // 何もしない
マウスのイベントが起こった座標を知るためには、受け取ったマウスイベントに対してgetXメソッド、getYメソッドを呼び出すことによって求めることができます。たとえば、変数eがマウスイベントを表している場合、次のように変数xとyに座標値を得ることができます。
int x = e.getX( ), y = e.getY( ) ;
たとえば、ある矩形(四角形)領域の中がクリックされたかどうかをチェックするためのプログラムの一部を記述してみましょう。インスタンス変数checkedが論理値を保持する変数で、その値がtrueになっていれば、その矩形の内部をユーザがクリックしたことを示すとします。このときに、mouseClickedメソッドは次のように記述します。記述では、矩形領域の左上の角が(10, 120 )、右下の角が( 40, 140 )となっています。
public void mouseClicked( MouseEvent e ) { int x = e.getX( ), y = e.getY( ); if ( x >= 10 && x <= 40 && y >= 120 && y <= 140 ) { checked = true ; } else { checked = false ; } }
mouseClickedメソッドで、マウスクリックに対処する場合は、何回クリックされたかということをgetClickCountメソッドを用いて確かめることができます。ダブルクリックや、トリプルクリックに対応することができます。たとえば、ダブルクリックのときに別の何かをさせたい場合は、次のように記述することになるでしょう。
public void mouseClicked( MouseEvent e ) { int count = e.getClickCount( ); if ( count == 1 ) { // シングルクリックのときにおこなうこと System.out.println( "Single Clicked" ); } else if ( count == 2 ) { // ダブルクリックのときにおこなうこと System.out.println( "Double Clicked" ); } }
マウスがクリックされた場所のx座標、y座標の整数値を画面に表示するだけのアプレットを作成してみましょう。インスタンス変数のcurrentxとcurrentyに、クリックされた座標値を保持しておくものとします。ビューであるpaintメソッドでは、それを数値として表示するようにしています。
import java.awt.*; import java.awt.event.*; import java.applet.*; public class MouseIn extends Applet implements MouseListener { int currentx, currenty; // x座標、y座標を保持 public void init( ) { addMouseListener( this ); // マウス入力に対応する currentx=0; currenty=0; // 仮に最初は( 0, 0 )としておく } public void paint( Graphics g ) { g.drawString( "Mouse Clicked At x:" + currentx + " y:" + currenty , 50, 100 ); } public void mouseClicked( MouseEvent e ) { currentx = e.getX( ); currenty = e.getY( ); repaint( ); } public void mousePressed( MouseEvent e ) { } // 何もしない public void mouseReleased( MouseEvent e ) { } // 何もしない public void mouseEntered( MouseEvent e ) { // 何もしない public void mouseExited( MouseEvent e ) { } // 何もしない }
今度は、クリックされた座標を数値で表示するのではなくて、そこに四角形を表示するようなアプレットを考えてみます。マウスだけではなくて、キー入力も受け付けましょう。矢印キーとマウスのクリックに反応して、四角形の表示位置を変えているようにしています。インスタンス変数のcurrentxとcurrentyが、それぞれ四角形を表示させるべき座標を保持しています。keyPressedおよびmouseClickedメソッドは共に、これらの値を変更して、repaintメソッドを呼び出して、再描画させています。また、インスタンス変数のmycolorとmouseEntered, mouseExitedの2つのメソッドを使って、マウスがアプレットの描画領域にあるときとないときで、色を変えています。
import java.awt.*; import java.applet.*; import java.awt.event.*; public class BoxMover extends Applet implements KeyListener, MouseListener { int currentx, currenty; Color mycolor; public void init( ) { addKeyListener( this ); addMouseListener( this ); currentx = 100; currenty=100; mycolor = Color.red; // 最初は赤 } public void paint( Graphics g ) { g.setColor( mycolor ); g.fillRect( currentx, currenty, 50, 50 ); } public void keyPressed( KeyEvent e ) { switch ( e.getKeyCode() ) { case KeyEvent.VK_UP: currenty -= 5; break; case KeyEvent.VK_DOWN: currenty += 5; break; case KeyEvent.VK_RIGHT: currentx += 5; break; case KeyEvent.VK_LEFT: currentx -= 5; break; default: break; } repaint( ); } public void mouseClicked(MouseEvent e) { currentx = e.getX( ); currenty = e.getY( ); repaint( ); } public void mouseEntered(MouseEvent e) { mycolor = Color.red; repaint( ); } public void mouseExited(MouseEvent e) { mycolor = Color.blue; repaint( ); } public void keyTyped( KeyEvent e ) { } public void keyReleased( KeyEvent e ) { } public void mousePressed( MouseEvent e ) { } public void mouseReleased( MouseEvent e ) { } }
マウスの動きに対応するには、クラスの定義のところで、MouseMotionListenerインターフェースを用います。アプレットがマウスの動きに対応することを登録するために、initメソッドの中でaddMouseMotionListenerメソッドを呼び出します。このメソッドの呼出しは、アプレット自身が対処することを示すために次のように記述します。
addMouseMotionListener( this ); // マウスの動きにアプレット自身が対応する
MouseMotionListenerインターフェースに対応して、マウスが動くイベントが発生したときに呼び出されるメソッドは次のように指定します。
▼マウスが動くときに対応するメソッドの書式
public void mouseDragged( MouseEvent e ) {
// マウスがドラッグされたときに行なうこと
}
public void mouseMoved( MouseEvent e ) {
// マウスが動いたときに行なうこと
}
2つメソッドがありますが、対処する必要のないイベント用のメソッドについては、何も記述する必要はありません。たとえば、次の記述はドラッグだけに対応しています。
public void mouseDragged( MouseEvent e ) { //マウスがドラッグされたときに行なうこと } public void mouseMoved( MouseEvent e ) { } // 何もしない
今度は、マウスが押された場所からドラッグを行ない、その場所に線を表示するアプレットを作ってみましょう。ドラッグしている最中も、サイズが変化する線を逐一表示します。これは、ドローイングを行なうソフトウェアでは基本操作として用いられているものです。
まず、マウスボタンが押されたときに、mousePressedが呼び出されます。そこで、始点として、インスタンス変数startx、startyにその座標値を保持しておきます。しかし、クリックして終わりではなく、ボタンを押した後に、続けてドラッグ操作が始まり、線を描くことになります。この間、ユーザはマウスのボタンを離すわけではありませんので、メソッドとしては、mouseClickedではなくて、mousePressedの方を用います。
次に、ドラッグしている最中は、mouseDraggedが何回も呼び出されます。そこで、終点(現在のマウスの位置)としてインスタンス変数endx、endyにその座標値を保持しておきます。マウスを離した時点で、このメソッドは呼ばれなくなりますので、離す直前のマウスの座標値で最終的な線が描かれることになります。
import java.awt.*; import java.awt.event.*; import java.applet.*; public class LineDraw extends Applet implements MouseListener, MouseMotionListener { int startx, starty, endx, endy; // 始点と終点のx,y座標 public void init( ) { addMouseListener( this ); addMouseMotionListener( this ); startx=0; starty=0; endx=0; endy=0; // 0で初期化する } public void paint( Graphics g ) { g.drawLine( startx, starty, endx, endy ); // 線を描く } public void mousePressed( MouseEvent e ) { startx = e.getX( ); starty = e.getY( ); // 始点を設定 endx = e.getX( ); endy = e.getY( ); // 終点も同じ座標に repaint( ); } public void mouseDragged( MouseEvent e ) { endx = e.getX( ); endy = e.getY( ); // 終点だけを変更 repaint( ); } public void mouseClicked( MouseEvent e ) { } public void mouseReleased( MouseEvent e ) { } public void mouseEntered( MouseEvent e ) { } public void mouseExited( MouseEvent e ) { } public void mouseMoved( MouseEvent e ) { } }
マウス操作で線を描画するようなアプレットを作りましたが、一本線を引く度に前に引いた線が消えてしまいます。配列を使って、前に引いた線を10本までは覚えておくようにしましょう。startx、starty、endx、およびendyがそれぞれ始点と終点のx、y座標値を覚えておくための配列になっています。変数currentは、今まで線を引いた本数を表しているのですが、同時に新しい線の座標値を覚えるための配列のインデックスとしても使われています。paintメソッドでは、今まで引いた本数分、配列の要素に覚えた座標値を元に線を描いています。なお、10本まで線を引いたら、currentを0にして御破算にしています。
import java.awt.*; import java.awt.event.*; import java.applet.*; public class LineDrawer extends Applet implements MouseListener, MouseMotionListener { int startx [] = new int[ 10 ], starty [] = new int[ 10 ]; int endx [] = new int[ 10 ], endy[] = new int[ 10 ]; int current = 0; public void init( ) { addMouseListener( this ) ; addMouseMotionListener( this ); } public void paint( Graphics g ) { for ( int i=0; ipublic void mousePressed( MouseEvent e ) { if ( current >= startx.length ) { current = 0; } startx[ current ] = e.getX( ); starty[ current ] = e.getY( ); endx[ current ] = e.getX( ); endy[ current ] = e.getY( ); current++; repaint(); } public void mouseDragged( MouseEvent e ) { endx[ current-1 ] = e.getX( ); endy[ current-1 ] = e.getY( ); repaint(); } public void mouseClicked( MouseEvent e ) { } public void mouseReleased( MouseEvent e ) { } public void mouseEntered( MouseEvent e ) { } public void mouseExited( MouseEvent e ) { } public void mouseMoved( MouseEvent e ) { } }
上記の何本も線を描けるアプレットを配列ではなく、ArrayListと要素にPointクラスを使って実現し、 本数の制約がないようにしてみて下さい。
マウスがクリックされたときや、キーが入力されたときに、修飾キーの状態がどうなっているかをチェックすることができます。修飾キー(Modifier Key)というのは、そのキー自体を押すことには意味がなく、別のキーを共に押されることによって、元のキーの意味を変えるという役割を持っています。たとえば、シフトキーは、英字と共に押されることによって、英字を小文字から大文字に変化させます。修飾キーとしては、つぎの4つが用意されています。
修飾キーの名称 | どのキーか |
---|---|
シフトキー | Shiftと書かれているキー |
コントロールキー | Controlとか、Ctrlとか書かれているキー |
Metaキー | Left/Right Metaキー、あるいはMacintoshのCommandキー |
ALTキー | Windows用のALTキー、あるいはMacintoshのOptionキー |
これらの修飾キーは、システムやアプリケーションによって、様々な意味や機能が割り振られています。アプレットの中で、ユーザからの入力イベントが発生したときに、これらの修飾キーの状態を確かめたい場合は、マウスイベントやキーイベントの共通のスーパークラスであるInputEventクラスに、これらの修飾キーの状態をチェックすることができるメソッドが用意されています。それを利用してみましょう。
メソッド名 | 判定の内容 |
---|---|
isShiftDown( ) | シフトキーが押されたかどうか |
isControlDown( ) | コントロールキーが押されたかどうか |
isMetaDown( ) | メタキーが押されたかどうか |
isAltDown( ) | ALTキーが押されていたかどうか |
これらのメソッドは呼び出されると、指定された修飾キーが、押されていればtrueを返します。押されていなければ、falseを返します。そのため、これらのメソッドの呼び出しは、たいていはif文とともに用いられます。しかも、メソッドが論理値を返すので、論理値と比較する必要はありません。このように論理値を直接メソッド呼出し結果として返してくるものは、if文の条件式に直接と記述できるのできます。
if ( e.isShiftDown( ) == true ) { ..... } // こう書くこともできるが if ( e.isShiftDown( ) ) { ..... } // 直接こう書くことができる
2つも3つも、マウスにボタンがある場合は、右ボタンや真ん中のボタンを押した場合は、Javaでは修飾キーを押しながら左ボタンを押したのであると認識されますが、Macintoshでは、ユーザを煩わせることがないように、基本的にはマウスボタンが一つしかありません。また、Webブラウザによっては、右ボタンを特別な機能のために用いているものもあります。結局、どのボタンが押されたかをチェックするよりも、上の修飾キーと組み合わせる方がより良い解決策になります。Macintoshでは一般的に、Controlキーを押しながらのクリックは右ボタンクリック、Optionキーを押しながらのクリックが中央ボタンクリックとして見なされます。
Windowsなどで、どうしてもマウスボタンを3つぐらい使うようなアプリケーションを作りたい場合は、 MouseEventで、getButton( )メソッドでどのボタンが押されたのか、整数値で返ってきますので、 その値を定数値と比較します。たとえば、次のような形です。
public void mousePressed( MouseEvent me ) { if ( me.getButton( ) == MouseEvent.BUTTON1 ) { // 左ボタンですること } else if ( me.getButton( ) == MouseEvent.BUTTON2 ) { // 中ボタンですること } else if ( me.getButton( ) == MouseEvent.BUTTON3 ) { // 右ボタンですること } }
上記のように、MouseEventクラスに以下のボタンの値が設定されています。
値 | 意味 |
---|---|
MouseEvent.NOBUTTON | どのボタンも押されていない |
MouseEvent.BUTTON1 | 左ボタン |
MouseEvent.BUTTON2 | 中ボタン |
MouseEvent.BUTTON3 | 右ボタン |
MouseWheelリスナーは、マウスのホイールの周り具合を視てくれるリスナーです。 MouseWheelListenerインターフェースを実装する形にします。
class MyMouseWheelAdapter implements MouseWheelListener { }
アプレットで対処する場合は、自身がリスナーになることをinitメソッドの中で追加します。
addMouseWheelListener( this );
MouseWheelListenerを実装したクラスでは、mouseWheelMovedメソッドを定義する必要があります。
public void mouseWheelMoved( MouseWheelEvent mwe ) { }
このmouseWheelMovedメソッドの中では、MouseWheelEventの変数(上記の例ではmwe)に対して、 getWheelRotationメソッドを呼び出して、回転量を得ることができます。 この回転量(クリック数)は、整数値で得られます。
public void mouseWheelMoved( MouseWheelEvent mwe ) { int count = mwe.getWheelRotation( ); if ( count > 0 ) { // 下方向の回転に対しての処理を行なう } else { // 下方向の回転に対しての処理を行なう } }
import java.awt.*; import java.awt.event.*; import java.applet.*; public class MultipleMouseDrag extends Applet implements ActionListener, MouseListener, MouseMotionListener { Rectangle rectangle [ ] = new Rectangle[ 50 ]; int count = 0; int startx, starty, endx, endy; public void init( ) { addMouseListener( this ); addMouseMotionListener( this ); Button button = new Button( "Reset" ); button.addActionListener( this ); add( button ); } public void paint( Graphics g ) { for ( int i = 0; i < count ; i ++ ) { g.drawRect( rectangle[ i ].x, rectangle[ i ].y, rectangle[ i ].width, rectangle[ i ].height ); } g.drawRect( smaller( startx, endx ), smaller( starty, endy ), difference( startx, endx ), difference( starty , endy ) ); } public void mousePressed( MouseEvent e ) { startx = endx = e.getX( ); starty = endy = e.getY( ); repaint( ); } public void mouseReleased( MouseEvent e ) { if ( count < 50 ) { rectangle[ count ++ ] = new Rectangle( smaller( startx, endx ), smaller( starty, endy ), difference( startx, endx ), difference( starty , endy ) ); } } public void mouseDragged( MouseEvent e ) { endx = e.getX( ); endy = e.getY( ); repaint(); } public void actionPerformed( ActionEvent e ) { startx = starty = endx = endy = 0; count = 0; repaint( ); } int smaller( int w, int u ) { return ( w < u ) ? w : u ; } // 小さい方を返します int difference( int w, int u ) { return ( w > u ) ? w - u : u - w; } // 差を返します public void mouseClicked( MouseEvent e ) { } public void mouseEntered( MouseEvent e ) { } public void mouseExited( MouseEvent e ) { } public void mouseMoved( MouseEvent e ) { } }
プログラムでは、smallerとdifferenceという2つのメソッドを自作で定義して、条件分岐をする手間を省いています。このような自作のメソッドの記述の仕方は次の章で説明します。これらの2つのメソッドで、短くプログラムを記述することができています。また、ボタンを1つ用意して、押されたら、それまで覚えていた内容を全部リセットしています。
ベジェ曲線(Bezier Curve)は制御点(Control-Point)と呼ばれる複数の点に基づく多項式で定義され、パラメータを0.0から1.0まで変化させるパラメトリック曲線の一つです。ベジェ曲線の多項式は、3次までがよく用いられます。一般に、n次のベジェ曲線を表すには、n+1個の制御点が必要で、3次の場合は、4個必要となります。3次のベジェ曲線の場合の方程式と、曲線上の各頂点の座標は制御点(P0〜P3)から以下のような式で計算します。
B( t ) = ( 1 - t ) 3P0 + 3t( 1 - t )2P1 + 3t2( 1 - t )P2 + t3P3
この式は、バーンスタイン関数と呼ばれています。それぞれのPiには、各制御点のx座標またはy座標が入ります。x座標とも、y座標とも同じ形の式で計算できるのです。これを使って、配列に格納されている4つの座標値を用いて、ベジェ曲線を描画するようなアプレットを作ってみました。paintメソッドの部分だけを示しています。1-tをいちいち記述するのは面倒なので、ct(Complement for t の意)という名前の変数に入れています。 折れ線で近似する方式で描きますが、曲線上の各頂点の座標を配列に求めています。 そして、ガイドとなる折れ線も、近似したベジェ曲線もdrawPolylineを使って描いています。
int px[ ] = { 30, 70, 130, 200 }; // 制御点のx座標の配列 int py[ ] = { 170, 50, 20, 165 }; // 制御点のy座標の配列 public void paint (Graphics g) { g.setColor( Color.blue ); g.drawPolyline( px, py, px.length ); // ガイドの折れ線 int n = 100; // 100本の直線で近似 double t, ct, delta; int bx[ ] = new int[ n+1 ]; int by[ ] = new int[ n+1 ]; delta = 1.0/n; t = 0.0; for ( int i=0; i <= n; i++ ) { ct = 1 - t; bx[ i ] = (int)( ct*ct*ct*px[ 0 ] + 3*t*ct*ct*px[ 1 ]+ 3*t*t*ct*px[ 2 ] + t*t*t*px[ 3 ] ); by[ i ] = (int)( ct*ct*ct*py[ 0 ] + 3*t*ct*ct*py[ 1 ]+ 3*t*t*ct*py[ 2 ] + t*t*t*py[ 3 ] ); t = t + delta; } g.setColor( Color.red ); g.drawPolyline( bx, by, bx.length ); }
上記のベジェ曲線の描画を、マウスが4回クリックされた点に表示するように 直してみて下さい。
コンポーネント関係のリスナーを無名クラスや内部クラスを使って記述するためのアダプター(Adapter)と呼ばれるクラスが用意されています。ComponetAdapter, ContainerAdapterは、それぞれコンポーネントやコンテナで発生する一般的なイベントを処理するためのものです。FocusAdapterは、ウィンドウが最前面に来た場合などに発生するFocusEventに対応したものです。
ComponentAdapater, ContainerAdapter, FocusAdapater, KeyAdapter,
MouseAdapater, MouseMotionAdapter, WindowAdapter
これらのクラスの中には、何もしないリスナー用のメソッドが定義されています。たとえば、MouseAdapterでしたら、マウスリスナーに必要な5つのメソッドが既に定義されています。これらのクラスをスーパークラスとして利用して、自分が作成した無名クラスや内部クラスの中で、必要なメソッドだけ上書きすればよいのです。
内部クラスは、1つのクラスの項目として扱われます。そのため、クラス側に属するのか、インスタンス側に属するのか(staticが付くかどうか)で扱いが微妙に変わります。インスタンス側につくものと、クラス側につくものと同じ内容で両方記述してみました。以下の内部クラスは、単に1234という整数値を返すメソッドを持つのみの単純なものです。まずインスタンス側に付く内部クラスです。内部クラスの生成の仕方の記述が今まで見たこともないような形で、オブジェクトを指す変数の後でピリオドで区切ってからnew演算子を書く形になっています。なお、インスタンス側のものは、内部クラスから外側のクラスのインスタンス変数を参照しやすく、メソッドを呼び出しやすくなっています。
public class InnerTester { public static void main( String [ ] a ) { InnerTester myself = new InnerTester( ); System.out.println( "" + (myself.new InnerSample( )).getValue( ) ); } class InnerSample { int getValue( ) { return 1234; } } }
内部クラスはコンパイルされると、包含されるクラス名に$がついて、その後に内部クラスの名前付いたようなクラスファイルが作成されます。たとえば、上記のインスタンスに属するの方の内部クラスについては、InnerTester$InnerSample.classという名前でクラスファイルができます。内部クラスは、スマートコンポーネントなどの記述に利用します。本来は、単体のクラスに分かれている必要がないもので、かつリスナーや描画メソッドを入れたいような場合に有効に利用できます。
無名クラスは、スーパークラスを指定しながら、その場でクラスの実体を定義するもので、 次のような書式に基づいて記述されます。
▼無名クラスの書式
new スーパークラスの名前 ( ) { クラスの定義 }
次のアプリケーションは、ウィンドウリスナーのうち、アクティベートに関するものだけを扱うようにしたものです。addWindowListenerのパラメータとして、無名クラスの書式が使われています。スーパークラスはアダプタを利用しています。入力が自分のウィンドウの中にあるときは、青色でその旨を表示し、そうでなければ、赤色で表示するようなプログラムになっています。
import java.awt.*; import java.awt.event.*; public class WindowTester extends Frame { Color color = Color.blue; public static void main( String a [ ] ) { new WindowTester( ); } public WindowTester( ) { super( "WindowTester" ); addWindowListener( new WindowAdapter( ) { public void windowActivated( WindowEvent e ) { color = Color.blue; repaint( ); } public void windowDeactivated( WindowEvent e ) { color = Color.red; repaint( ); } } ); setSize( 200, 200 ); setVisible( true ); } public void paint( Graphics g ) { g.setColor( color ); g.drawString( color.equals( Color.blue ) ? "Activated" : "Deactivated", 40, 100 ); } }
無名クラスはコンパイルされると、包含されているクラス名に$記号がついて、順次番号が振られたクラスとしてクラスファイルができます。たとえば、上の場合でしたらWindowTester$1.classという名前でクラスファイルができます。
サブパネルの中でグラフィックス描画を行ないたいときは、 そのパネルに対応したクラスを定義して、 その中に、initメソッドやpaintメソッドを用意して、描画などに対応する必要があります。 自分自身で描画メソッドを持つ、 このようなコンポーネントをスマートコンポーネント(smart component)と呼びます。
以下の例では、Panelクラスのサブクラスとして、PackmanPanelクラスを自作で用意し、このクラスの中で再描画のときに呼ばれるpaintメソッドを定義したものです。この例は、アプリケーションとして記述していますが、アプレットでも同様のことは可能です。
import java.awt.*; import java.awt.event.*; public class PanelTester extends Frame implements ItemListener { PackmanPanel view; String sname [ ] = { "small", "regular", "big", "huge" }; int sizes [ ] = { 10, 50, 100, 250 }; Choice c; public static void main( String [ ] arg ) { new PanelTester( ); } public PanelTester( ) { super( "Packman Panel Tester" ); setLayout( new BorderLayout( ) ); c = new Choice( ); for ( int i=0; i < sname.length; i++ ) { c.add( sname[ i ] ); } c.select( 0 ); c.addItemListener( this ); view = new PackmanPanel( ); add( "North", c ); add( "Center", view ); setSize( 500, 500 ); setVisible( true ); view.repaint( ); } public void itemStateChanged( ItemEvent e ) { view.setSize( sizes[ c.getSelectedIndex( ) ] ); view.repaint( ); } } class PackmanPanel extends Panel { int size =0; public void setSize( int s ) { size = s; } public void paint( Graphics g ) { g.setColor( Color.yellow ); g.fillArc( getWidth()/2-size, getHeight()/2-size, size*2, size*2, 45, 270 ); } }
アプリケーションでグラフィックスを使いたい場合は、Frameクラスを用います。 これが、通常のウィンドウを表現するためのクラスになっています。 次のようにして実行クラスを定義します。
- Appletクラスではなくて、Frameクラスのサブクラスとしてクラスを定義する
- initメソッドの替わりにコンストラクタを用いて初期化する
- コンストラクタの中で、superコンストラクタを使ってウィンドウを生成する
- コンストラクタの中で、setSizeメソッドとshowメソッドを呼び出す
- mainメソッドから、自クラスのインスタンスを1つ作る
後は、アプレットと同様のプログラミングで行なえます。描画は、アプレット同様にpaintメソッドの中で行ないます。アプリケーションの場合は、Frameクラスのサブクラスとして定義していますので、標準のレイアウトがボーダーレイアウトとなります。
次のアプリケーションは、ウィンドウを出して、ただ単に線を引くだけのものです。 ここには出しませんでしたが、アプリケーションでも通常のアプレットのように、 コンポーネントを配置することができます。 ボタンなどがある場合は、アプレットと同様にActionListenerなどを使って、対応するメソッドを用意します。
import java.awt.*; public class SimpeFrame extends Frame { public static void main( String [ ] args ) { new SampleFrame( ); } public SimpleFrame( ) { super( "Sample" ); setSize( 300, 300 ); setVisible( true); } public void paint( Graphics g ) { g.setColor( Color.blue ); g.drawLine( 10, 10, 200, 200 ); } }
ウィンドウリスナーを用意する:ウィンドウリスナー(WindowListener)の場合には、ウィンドウイベント(WindowEvent)に対応して次のようなメソッドを用意する必要があります。ウィンドウがアクティベートされたというのは、そのウィンドウでキー入力を受け付けるようになったことを示します。windowClosingは、クローズボックスが押されたときに、windowClosedは、ウィンドウが閉じられた直後に呼ばれるメソッドです。
windowActivated・windowDeactivated ウィンドウにアクティベート関して
windowOpened・windowClosed・windowClosing ウィンドウの開閉に関して
windowDeiconified・windowIconified ウィンドウのアイコン化に関して
通常別ウィンドウを開く場合は、必ず以上のウィンドウリスナーを用意しておく必要があります。なお、コンポーネントリスナー(ComponentListener)を用いれば、コンポーネントイベント(ComponentEvent)で定義されている次のような場合に対処することができます。
componentHidden・componentShown コンポーネントの可視制御に関して
componentMoved コンポーネントの座標移動に関して
componentResized コンポーネントのサイズ変更に関して
以下のプログラムは、簡単なウィンドウを持つアプリケーションにウィンドウリスナーを追加したものです。
import java.awt.*; import java.awt.event.*; public class StandardFrame extends Frame implements WindowListener { public static void main( String [ ] args ) { new StandardFrame( ); } public StandardFrame( ) { super( "Sample Window" ); addWindowListener( this ); setSize( 300, 300 ); setVisible( true ); } public void paint( Graphics g ) { g.setColor( Color.blue ); g.drawLine( 10, 10, 200, 200 ); } public void windowActivated( WindowEvent we ) { repaint( ); } public void windowDeactivated( WindowEvent we ) { repaint( ); } public void windowOpened( WindowEvent we ) { repaint( ); } public void windowClosed( WindowEvent we ) { System.exit( 0 ); } public void windowClosing( WindowEvent we ) { setVisible( false ); displose( ); } public void windowIconified( WindowEvent we ) { } public void windowDeiconified( WindowEvent we ) { repaint( ); } }
以下の簡単な例は、ウィンドウの大きさが変えられたり(componentResized)、タイトルバー上にあるクローズボックスが押されたり(windowClosing)したとき対処するアダプターを内部クラスで実現したものです。2つのアダプターのうち一方はインスタント側に、もう一方はクラス側に付けています。記述の仕方の違いなどを参照してください。ウィンドウの大きさが変えられるとそれに従って青いボックスも表示位置を必ず中央に表示されるように変えます。また、ボックスの大きさよりもウィンドウの大きさが小さくなるのを防いでいます。
import java.awt.*; import java.awt.event.*; public class AdjustableFrame extends Frame { public static void main( String [ ] args ) { new AdjustableFrame( ); } public AdjustableFrame( ) { super( "Adjustable Frame" ); setSize( 300, 300 ); setVisible( true ); Adjuster complistener = new Adjuster( ); addComponentListener( complistener ); CloseBoxAdapter terminator = new CloseBoxAdapter( ); addWindowListener( terminator ); } public void paint( Graphics g ) { g.setColor( Color.blue ); int x = getSize( ).width / 2 - 40; int y = getSize( ).height / 2 - 40; g.fillRect( x, y, 80, 80 ); } class Adjuster extends ComponentAdapter { public void componentResized( ComponentEvent ce ) { if ( getSize( ).width < 80 ) { setSize( 80, getSize( ).height ); } if ( getSize( ).height < 80 ) { setSize( getSize( ).width, 80 ); } repaint( ); } } class CloseBoxAdapter extends WindowAdapter { public void windowClosing( WindowEvent we ) { setVisible( false ); } public void windowClosed( WindowEvent we ) { dispose( ); System.exit( 0 ); } } }
以下のようなimport文が必要になります。
import javax.swing.*; import javax.swing.event.*; // swingのeventを使う場合
AWTと互換性がある(あるいは拡張された)コンポーネントは以下のようになります。
AWTのコンポーネントクラス | Swingのコンポーネントクラス | 内容 |
---|---|---|
Applet | JApplet | アプレット |
Button | JButton | ボタン |
Checkbox | JCheckbox | チェックボックス |
Choice | JComboBox | ポップアップメニュー(と入力) |
Component | JComponent | 共通のスーパークラス |
Dialog | JDialog | ダイアログ |
Frame | JFrame | フレーム |
Label | JLabel | ラベル |
List | JList | リスト |
MenuBar | JMenuBar | メニューバー |
MenuItem | JMenuItem | メニュー項目 |
Panel | JPanel | パネル |
PopupMenu | JPopupMenu | メニューバー |
RadioButton | JRadioButton | ラジオボタン |
Scrollbar | JScrollbar | スクロールバー |
TextArea | JTextArea | スクロールバー |
TextField | JTextField | スクロールバー |
Window | JWindow | ウィンドウ |
Swingによって新たに追加されたコンポーネントは、以下のようになります。
Swingのコンポーネントクラス | 内容 |
---|---|
JDesktopPane | デスクトップペーン |
JEditorPane | エディタペーン |
JLayeredPane | レイヤー化されたペーン |
JOptionPane | オプションのペーン |
JScrollPane | スクロールペーン |
JSplitPane | 分割されるペーン |
JRootPane | ルートペーン |
JTabbedPane | タブ化されたペーン |
JTextPane | テキストペーン |
JColorChooser | カラー選択 |
JFileChooser | ファイル選択 |
JInternalFrame | 内部フレーム |
JPasswordField | パスワード入力のためのテキストフィールド |
JProgressBar | 実行経過の比率を表示するプログレスバー |
JSeparator | セパレータ |
JSlider | スライダー |
JSpinner | スピンナー |
JTable | 表 |
JToolBar | ツールバー |
JTree | 木構造 |
JViewPoint | ビューポイント |
JSliderは、ChangeListenerを必要とします。 このChangeListenerが実装されたクラスにおいて、stateChanged( ChangeEvent ce ) メソッドがスライダーを動かしたときに呼ばれます。 これとJPanelを利用して、パックマンを表示するスマートコンポーネントの形のアプレット を作ってみました。
import java.applet.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; public class PackApplet extends Applet { PackmanPanel pp; Panel p; JSlider slider; // グリッドレイアウトで制御パネルと表示パネルを配置 public void init( ) { setLayout( new GridLayout( 1, 2 ) ); p = new Panel( ); // こちらは通常のパネル slider = new JSlider( 10, 200, 100 ); // スライダーを作る slider.addChangeListener( new MyChangeAdapter( ) ); p.add( slider ); add( p ); pp = new PackmanPanel( ); add( pp ); } // 内部クラスでスマートコンポーネントのJPanelを実現する class PackmanPanel extends JPanel { public void paint( Graphics g) { g.setColor( Color.yellow ); int size = slider.getValue( ); // スライダーの値を参照 g.fillArc( getWidth( ) / 2 - size /2, getHeight( ) / 2 - size /2, size, size, 45, 270 ); } } // 内部クラスでChangeListenerを実装をする class MyChangeAdapter implements ChangeListener { public void stateChanged( ChangeEvent ce ) { repaint( ); } } }
Swingによって追加されたレイアウトは以下のようになります。
追加されたレイアウト | 内容 |
---|---|
BoxLayout | ボックスレイアウト |
OvelayLayout | ボックスレイアウト |
SpringLayout | ボックスレイアウト |
ViewportLayout | ボックスレイアウト |
<<Previous Lecture | >>Next Lecture |