MIDPではキー入力の方法がいくつか存在します。いくつか存在していて一体どれを使えば良いのか混乱してしまいそうなので、順番に説明していきます。
Canvasクラスには
という関数が準備されています。アプリ側でこの関数を実装(オーバーライド)するとキーイベントがここにやってくることになります。
※といってもMIDletクラスでDisplay.setCurrent()にてカレントディスプレイに設定されている場合だけです。
上記関数に渡されるキーイベントはint型の数値であり、Canvasクラスに定義されています。WTKのDefaultColorPhoneでは以下の様に定義されて
います。
OAPも以下のものとまったく同じ値が採用されています。
実はMIDPにおけるCanvasクラスで正式に定義されているものは上記KEY0〜KEY9、*#だけになります。但しWTKもOAPも独自に他のキーも拡張されています。実際の対比表としては以下になります。
本来、上下左右キー等はMIDPの仕様としては明確に定義されていません。従って上記のマイナスの部分の数値はOAP独自仕様という風に解釈できます。しかしこのままでは上下左右キーを使った「全世界で共通的に動作するMIDPアプリ」を構築する事が出来ません。(もちろん、2,4、6、8キーを使えば問題ないのです)
極端に言えば、テンキーだけの端末も世界にはあるのですから、-5という数値が来るなんて事はまったく期待できない訳です。
そこでMIDP2.0からはCanvas.getGameAction()という変換関数ができました。MIDP2.0では以下の様な標準キーを想定しており、上記キーイベント値をgetGameAction()に入れると、標準の入力値に変換してくれます。
どのキーが上記ゲームキーに対応されるかは、ずばりOEM次第ですが、とりあえずUPキーは上をイメージされるキーに割り当てられるでしょう。
OAPではそれぞれ以下の様に割り当てられています。これはWTKと同じ配置になります。
従ってOAP(またはWTKのDefaultColorPhone)では以下のコードは等価になります。
public void keyPressed( int keycode ) { switch ( getGameAction( keycode ) ) { case FIRE: a.webAccess(); break; case DOWN: a.exitApplet(); break; default: break; } }
public void keyPressed( int keycode ) { switch ( keycode ) { case -5: a.webAccess(); break; case -2: a.exitApplet(); break; default: break; } }
ただ、等価なのは偶々OAPがそのような仕様になっている為で、世界中のMIDP2.0端末では-5がFIREに割り当てられている保証はありません。
OAPではその他にもクリアキー、発信キーにイベント値が割り当てられているのですが、これらには標準の定義がありません。従ってこれらのキーを扱うには以下のようなコードを記述する必要があります。このようなコードを書いた場合、他のプラットフォームで正しく動作する保証はなくなります。
public void keyPressed( int keycode ) { switch ( keycode ) { case -8: // CLRキーが押下された break; case -10: // 発信キーが押下された break; default: break; } }
キーを長押しすると、keyRepeated()が呼び出されます。最初の一回目は約0.5秒後、その後は約0.1秒毎にイベントが発生します。この数値はOAPがBREWの上で構築されていて、そのBREWの仕様として以下の間隔が設定されているため、他のプラットフォームでは間隔が違う可能性があります。
上記仕様が自分の作成しているアプリにぴったりマッチしているのであれば、keyRepeated()も使いようがあります。マッチしないのであれば何らかの方法を検討しなくてはなりません。 例えばテトリスの様なゲームで、各ピースを左右に移動させる場合を想像してみます。普通にkeyRepeated()を使うと、1回目と2回目以降の移動間隔が違ってきます。このためこういったゲームを作る際は定間隔でのキー入力方式が欲しくなります。またキーの間隔はプラットフォームに依存します。プラットフォームに依存しない為にも定間隔キーイベント方法は必要になります。
例えば100msec間隔でキー押下状態を探りたい場合、Timer、TimerTaskを使うと以下の様になります。
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.microedition.lcdui.game.*; import java.util.*; import java.util.Timer.*; /* ========================================================================= */ public final class applet extends MIDlet implements CommandListener { private static Command cmdExit = new Command("Exit", Command.EXIT,1); private static Display display; /* ----------------------------------------------------------------------*/ public applet() { display = Display.getDisplay(this); mycanvas canvas = new mycanvas(); canvas.addCommand(cmdExit); canvas.setCommandListener(this); display.setCurrent( canvas ); } /* ----------------------------------------------------------------------*/ public void startApp() {} public void pauseApp() {} public void destroyApp( boolean flag ) {} /* ----------------------------------------------------------------------*/ public void commandAction(Command c, Displayable d) { try { destroyApp(true); notifyDestroyed(); } catch ( Exception e) {} } } /* ========================================================================= */ class mycanvas extends GameCanvas implements Runnable { private Timer myTimer; private TimerTask myTimerTask; mycanvas() { super( false ); myTimer = new Timer(); myTimerTask = new KeyTimerTask(this); myTimer.schedule(myTimerTask, 100, 100); } public void paint( Graphics g ) { g.setColor( 255, 255, 255 ); g.fillRect( 0, 0, getWidth(), getHeight() ); // 以下略 } public void run() { } public void keyPressed( int keycode ) { // 省略 } public void timerKeyPressed( int keycode ) { // 定間隔でのキー入力処理 System.out.println( "timerKeyPressed : " + keycode ); } } /* ========================================================================= */ class KeyTimerTask extends TimerTask { mycanvas canv; public KeyTimerTask( mycanvas canv ) { this.canv = canv; } public void run() { canv.timerKeyPressed( canv.getKeyStates() ); } }
別にここはTimerTaskの変わりにThreadを使っても問題ないと思います。しかしThreadはRunableのクラスをまわすことが出来ますが、TimerはTimerTaskのみが利用可能という違いがあります。またTimerはThreadと違って途中で一旦停止の様な動作が簡単に行えます。一長一短なので上手く使い分けて下さい。