モバイルのもと

Canvasクラス

複数Canvasの切替

MIDletの基本はひとつのMIDletに複数のDisplayableなクラスを切り替えていく形になると思います。例えば以下の様な感じです。

もちろん工夫すれば一枚のCanvasで複数画面構成を構築できます。実際、携帯Java初期の時代にはメモリ節約の為に複数のClassを作らないようにしていたようです。

ただ今の時代ですから生産性を高める為に複数のCanvasを生成するのも一考すべき内容です。具体的には以下の様なコードになります。

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public final class applet extends MIDlet implements CommandListener {

  private static Command cmdExit = new Command("Exit", Command.EXIT,1);

  private static Command cmdCanv1 = new Command("Go 1", Command.ITEM,1);
  private static Command cmdCanv2 = new Command("Go 2", Command.ITEM,1);
  private static Display display;

  // Canvasのインスタンス作成
  mycanvas1 canv1 = new mycanvas1();
  mycanvas2 canv2 = new mycanvas2();

  /* ----------------------------------------------------------------------*/
  public applet() {
    display = Display.getDisplay(this);

    canv1.addCommand(cmdExit);         // アプリ終了コマンド
    canv1.addCommand(cmdCanv2);        // Canvas2への移行コマンド
    canv1.setCommandListener(this);    // リスナーをこのクラスに設定

    canv2.addCommand(cmdCanv1);        // Canvas2への移行コマンド
    canv2.setCommandListener(this);    // リスナーをこのクラスに設定

    display.setCurrent( canv1 );       // 最初にCanv1をDisplayに設定する
  }
  
  public void startApp() {}
  public void pauseApp() {}
  public void destroyApp( boolean flag ) {}

  /* ----------------------------------------------------------------------*/
  public void commandAction(Command c, Displayable d) {

    if( c == cmdExit )
    {
      try
      {
        destroyApp(false);
        notifyDestroyed();
      }
      catch( Exception e ) {}
      
    } else if( c == cmdCanv1 ) {
      Display.getDisplay(this).setCurrent( canv1 );
    } else if( c == cmdCanv2 ) {
      Display.getDisplay(this).setCurrent( canv2 );
    }
  }
}

// Canvas1
class mycanvas1 extends Canvas {
  mycanvas1() {}
  
    public void paint( Graphics g ) {
    g.setColor( 255, 255, 255 );
    g.fillRect( 0, 0, getWidth(), getHeight() );

    g.setColor( 0, 0, 0 );
    g.drawString("canvas 1", 0, 0, Graphics.LEFT|Graphics.TOP);
  }
}

// Canvas2
class mycanvas2 extends Canvas {
  mycanvas2() {}
  
    public void paint( Graphics g ) {
    g.setColor( 255, 255, 255 );
    g.fillRect( 0, 0, getWidth(), getHeight() );

    g.setColor( 0, 0, 0 );
    g.drawString("canvas 2", 0, 0, Graphics.LEFT|Graphics.TOP);
  }

}

実行結果は以下の様になります。

 

左がCanvas1で、SOFT2キーを押下するとCanvas2キーが現れます。ExitコマンドはCanvas1にだけ付与してあります。

CanvasクラスからMIDletクラスメソッドの参照

複数のCanvasから共通の処理を行う際には、どこかひとつに纏めておきたいものです。例えば以下の様な例です。

どのキャンバスからもサイトへのアクセスを行うようにします。

Commandを利用すればこれは簡単に実装できます。

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public final class applet extends MIDlet implements CommandListener {

  private static Command cmdExit = new Command("Exit", Command.EXIT,1);
  private static Command cmdWebAccess = new Command("Site", Command.ITEM,1);
  private static Display display;

  /* ----------------------------------------------------------------------*/
  public applet() {
    display = Display.getDisplay(this);
    mycanvas canvas = new mycanvas();

    canvas.addCommand(cmdExit);
    canvas.addCommand(cmdWebAccess);
    canvas.setCommandListener(this);

    display.setCurrent( canvas );
  }
  
  public void startApp() {}
  public void pauseApp() {}
  public void destroyApp( boolean flag ) {}

  /* ----------------------------------------------------------------------*/
  public void commandAction(Command c, Displayable d) {
    if( c == cmdExit )
    {
      try
      {
        destroyApp(false);
        notifyDestroyed();
      }
      catch( Exception e ) {}
      
    } else if( c == cmdWebAccess ) {
      webAccess();
    }
  }
  
  /* ----------------------------------------------------------------------*/
  public void webAccess() {
    try {
      platformRequest( "http://urana.info/m/" );
    } catch (Exception e) {}
  }
}


class mycanvas extends Canvas {
  mycanvas() {}
  
    public void paint( Graphics g ) {
    g.setColor( 255, 255, 255 );
    g.fillRect( 0, 0, getWidth(), getHeight() );

    g.setColor( 0, 0, 0 );
    g.drawString("WebAccessTest", 0, 0, Graphics.LEFT|Graphics.TOP);
  }
}

上記コードで言えば、cmdWebAccessをCanvasに貼り付けて行けば、そのCanvasからはCommandメニューによっていつでもウェブにアクセスできるようになります。

しかし、ゲーム等を作っていると「美的感覚的にどうしてもメニューではなくて、キー操作によって実現したい」と考えるときもあるでしょう。そこで上記コードを改変して以下の様なコードを書いてみます。

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public final class applet extends MIDlet implements CommandListener {

  // 〜〜省略〜〜
  
  /* ----------------------------------------------------------------------*/
  public void webAccess() {
    try {
      platformRequest( "http://urana.info/m/" );
    } catch (Exception e) {}
  }
}


class mycanvas extends Canvas {
  mycanvas() {}
  
  // 〜〜省略〜〜

    public void keyPressed( int keycode ) {
        switch ( getGameAction( keycode ) ) {
      case FIRE:
        applet.webAccess();
        break;

      default:
        break;
    }
  }
}

これを単純にコンパイルすると以下のようなコードが吐き出されます。

プロジェクトの設定を保存しました
ビルド中 "skelton"
C:\WTK22\apps\skelton\src\applet.java:64: static でない メソッド webAccess() を static コンテキストから参照することはできません。
applet.webAccess();^
エラー 1 個
com.sun.kvem.ktools.ExecutionException
ビルドに失敗しました

最初は色々と考え込んでしまうのですが、この「applet.webAccess()」関数自体はコンパイル時にその場所を決定できないことに起因しています。(この辺りはC/C++ユーザーの方が理解しやすいのかもしれません)

このコンパイルエラーを絵で記すと以下の様になります。

class appletを定義はしましたが、これはあくまでclassであって、実体(インスタンス)を持つのは実行時です。なのでwebAccess()関数の場所が決定されるのは実行時、という事になります。

エラー文が分かりづらいのですが、これは当たり前の様にいつもやっている事を思い出せば、なんだ、と感じるかもしれません。以下、いつもやっていることです。

    canv1.addCommand(cmdExit);         // アプリ終了コマンド
    canv1.addCommand(cmdCanv2);        // Canvas2への移行コマンド
    canv1.setCommandListener(this);    // リスナーをこのクラスに設定

    canv2.addCommand(cmdCanv1);        // Canvas2への移行コマンド
    canv2.setCommandListener(this);    // リスナーをこのクラスに設定

    display.setCurrent( canv1 );       // 最初にCanv1をDisplayに設定する

先ほど出てきたコードなのですが、Canvas.addCommand()ではなくて、実インスタンスを保持しているcanv1やcanv2に対してメンバー関数を呼び出しています。

ついでなので、以下の様なコードを試してみます。

  /* ----------------------------------------------------------------------*/
  static public void webAccess() {
    try {
      platformRequest( "http://urana.info/m/" );
    } catch (Exception e) {}
  }

webAccess()をstatic関数にしたため、コンパイル時に関数の場所が決定されます。しかし、次に出るエラーは以下の通りです。

プロジェクトの設定を保存しました
ビルド中 "skelton"
C:\WTK22\apps\skelton\src\applet.java:45: static でない メソッド platformRequest(java.lang.String) を static コンテキストから参照することはできません。
platformRequest( "http://urana.info/m/" );^
エラー 1 個
com.sun.kvem.ktools.ExecutionException
ビルドに失敗しました

この問題を解決するには、class applet のインスタンスを取得して、そのインスタンスのwebAccess()を呼び出せば上手く行きます。 これまでのコードを書き直して、アプリの終了もキーで行えるようにしてみました。

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public final class applet extends MIDlet {

  static private applet instance;

  /* ----------------------------------------------------------------------*/
  public applet() {
  
    instance = this;
  
    Display display = Display.getDisplay(this);
    mycanvas canvas = new mycanvas();
    display.setCurrent( canvas );
  }
  
  public void startApp() {}
  public void pauseApp() {}
  public void destroyApp( boolean flag ) {}

  /* ----------------------------------------------------------------------*/
  // ウェブブラウザを起動する
  public void webAccess() {
    try {
      platformRequest( "http://urana.info/m/" );
    } catch (Exception e) {}
  }
  /* ----------------------------------------------------------------------*/
  // アプリを終了させる
  public void exitApplet() {
    try
    {
      destroyApp(false);
      notifyDestroyed();
    }
    catch( Exception e ) {}
  }
  
  /* ----------------------------------------------------------------------*/
  static public applet getInstance() {
    return instance;
  }
}


class mycanvas extends Canvas {
  mycanvas() {}
  
    public void paint( Graphics g ) {
    g.setColor( 255, 255, 255 );
    g.fillRect( 0, 0, getWidth(), getHeight() );
    g.setColor( 0, 0, 0 );
    g.drawString("WebAccessTest", 0, 0, Graphics.LEFT|Graphics.TOP);
  }

    public void keyPressed( int keycode ) {
    applet a = applet.getInstance();

        switch ( getGameAction( keycode ) ) {
      case FIRE:
        a.webAccess();
        break;
        
      case DOWN:
        applet.getInstance().exitApplet();  // 省略してこんな書き方でもOK
        break;

      default:
        break;
    }
  }
}

staticでアプリのインスタンスを持っているので、もしVMが複数アプレットを生成したら書き換えられてしまうのですが、エントリポイントでもあるこのクラスは複数生成されることはありません。

これでCommandメニューに頼らず、Canvas共通の動作を実行する事ができました。


モバイルの素へ戻る MIDP Scrollトップへ戻る


Google
MIDP2.0 CLDC1.1 JSR WWW

Copyright 2007 Mobile no THU