google android アプリケーション4. android...

50
1実験目的 111 1 View SurfaceView クラスを使ったプログラム(最首担当) 2View クラスを使ったプログラム 111 (含む: レポートの構成について) 3SurfaceView クラスを使ったプログラム 127 2 部 独自の View を作ろう(飯塚担当) 4ウォーミングアップ(簡単なアプリで構造を復習しましょう) 137 5ソートを行うアプリを作ってみましょう 137 6再帰処理を使ってコッホ曲線を描こう 145 7地雷ゲームを作ってみましょう 151 Saishu 2011/04/01 テーマ 3 Google Android アプリケーション 109

Upload: others

Post on 07-Jul-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

1. 実験目的 111 第 1 部 View と SurfaceView クラスを使ったプログラム( 首担当) 2. View クラスを使ったプログラム 111

(含む: レポートの構成について) 3. SurfaceView クラスを使ったプログラム 127 第 2 部 独自の View を作ろう(飯塚担当) 4. ウォーミングアップ(簡単なアプリで構造を復習しましょう) 137 5. ソートを行うアプリを作ってみましょう 137 6. 再帰処理を使ってコッホ曲線を描こう 145 7. 地雷ゲームを作ってみましょう 151

Saishu 2011/04/01

テーマ 3

Google Android アプリケーション

109

Page 2: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム
Page 3: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

情報学実験Ⅱ Google Android アプリケーション

1.実験目的 ( 首担当部分の実験目的)

View クラスのドキュメントを読み、概要を理解する。 View クラスを継承するクラスでカスタムウィジェットを作り、表示する。 カスタムウィジェットの描画性能を以下の 2 点から調べる。 ボールの総数による比較 ボールを作成することによる描画性能への影響 (応用:時間的に余裕のある学生への課題の目的) SurfaceView クラスでカスタムウィジェットを作り、表示する。 このカスタムウィジェットの描画性能を以下の3点から調べる。 View によるカスタムウィジェットとの描画性能の比較 ボールの総数による比較 ボールを作成することによる描画性能への影響 (飯塚担当部分の実験目的) View を継承した独自の View を使い、ソートのアルゴリズムについて確認する。また同

様の手法でメソッドの再帰呼び出しについて確認し、コッホ曲線を描く方法について学

習する。応用的なアプリケーションの作成として、地雷ゲームを作成する。ここで Android特有の記述について理解する。達成目標は以下のとおりである。

1. 独自の View を利用したアプリケーションを作成できるようになる 2. ソートのアルゴリズムを理解できる 3. メソッドの再帰呼び出しについて理解できる 4. Android のデバイス制御やイベントについて理解できる

第 1 部 View と SurfaceView クラスを使ったプログラム 参考資料 「Google アンドロイド Android プログラミング入門」ASCII

著者:株式会社豆蔵 (著者 8 人) p.331-337 この資料のソースコードは主にこの本から引用

2. View クラスを使ったプログラム

2.1 View クラスのドキュメントを読む (この中の問題は android-sdk-windows¥docs からたどって調べること) android-sdk-windows¥docs¥index.html をダブルクリックすると、「android developers」の画面になる(下図)。ここで「Reference」を選択する。左ウィンドウ上端の「Classe Index」 を選択すると、アルファベットでクラス名を指定できるので「v」を選ぶ。 以下の問題で翻訳する問題は翻訳ソフトを使い、不適切な翻訳は自分で修正する方法で回

答しなさい。

111

Page 4: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

View クラスのドキュメントの先頭は以下となっている。 public class View extends Object

implements Drawable.Callback KeyEvent.Callback Known Direct Subclasses

AnalogClock, ImageView, KeyboardView, ProgressBar, SurfaceView, TextView, ViewGroup, ViewStub これで View クラスの継承、インターフェース、View クラスを継承するサブクラスが分か

る。 View クラスの概要 This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling. View is the base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.). The ViewGroup subclass is the base class for layouts, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties. 問題 1 実験のレポートで使用する翻訳ソフト名を書きなさい。 問題 2 上記の「View クラスの概要」を翻訳しなさい。 「View のオブジェクトはスクリーンの長方形の領域を占有し、描画、イベント操作に応答

する」と書かれている。 問題 3 View クラスの Nested Classes のクラスは幾つあるか? その 1 つを取り上げ、説明文の英文を書き、翻訳しなさい。(下図を参考に)

問題 4 View クラスの XML Attributes は幾つあるか? その 1 つを取り上げ、その Description の英文を書き、翻訳しなさい。

112

Page 5: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

問題 5 View クラスの Constants は幾つあるか? 問題 6 View クラスの Public Constructors は幾つあるか? 問題 7 View クラスの Public Methods は幾つあるか? ここでは、View クラスのメソッド(名前を xxYyy)を、View#xxYyy メソッドと呼ぶ。 このプログラムでは描画のメソッド View#onDraw、タッチイベントを処理するメソッド View#onTouchEvent を使用する。 問題 8 ドキュメントの View#onDraw、View#onTouchEvent メソッドの英文の説明文を書

き、翻訳文しなさい。

2.2 View を継承したカスタムウィジェットを利用するアクティビティ このアプリケーションの重要点: ここでは View クラスを継承したカスタムウィジェットの理解を目的とする。

2.2.1 アプリケーションの構造 Activity クラスを継承する「JikkenActivity」クラス Activity クラスは、Java の「main」メソッドを含むクラスに相当 ① View クラスを継承する「JikkenView」クラス ② SurfaceView クラスを継承する「JikkenSurfaceView」クラス

これは 3 章で説明する。SurfaceView クラスは View クラスを継承する

SurfaceView クラスは画面に線、図形、画像を描画 View クラスは「Android」の表示部品の基本になるクラス View クラスを継承するクラスには、ボタン、リストなどのオブジェクト(ウィジェ

ット)と、配置のためのレイアウトなどのクラスがある。

実線矢印 継承 点線矢印 実装 実線四角 クラス 点線四角 インターフェース

113

Page 6: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

2.2.2 JikkenActivity クラス

Activity クラスは 画面の表示領域を確保 その上にボタン、リスト、画像を配置 ユーザからの操作を受けて、各種操作を行う。 Windows OS と Android OS のウィンドウの違い Windows OS : 複数のウィンドウが画面に表示 Android OS : 「Activity」が画面を覆う唯一のウィンドウ

Activity と Context Activity クラスは Context クラスを継承 Context クラス(アブストラクトクラス)にはアプリケーション自身の情報が格納 Context クラス にはファイルシステムにアクセスするメソッドを含む プロジェクトの設定、その他 「ファイル/新規/その他」を選択、「新規」画面から「Android/Android プロジェクト」を

選択 「新規 Android プロジェクト」画面で以下を設定 プロジェクト名: jikken2 ビルドターゲット: Android1.6 をクリック アプリケーション名:JikkenActivity パッケージ名: jp.ac.meisei.johojikken2 CreateActivity: JikkenActivity (1)アクティビティ JikkenActivity.java の編集 Viewを使ったAndroidアプリケーションでのアクティビティはデフォルトのアクティビテ

ィを利用する。以下がそのプログラムである。このようになっていることを確認する。 左端の「:」までは行番号 1: package jp.ac.meisei.johojikken2; 2: import android.app.Activity; 3: import android.os.Bundle; 4: public class JikkenActivity extends Activity { 5: @Override 6: public void onCreate(Bundle savedInstanceState) { 7: super.onCreate(savedInstanceState); 8: setContentView(R.layout.main); 9: } 10: //ここに menu 処理のプログラムを挿入 11: } 問題 9 このプログラムのファイル名を書け

114

Page 7: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

問題 10 このプログラムのパッケージは何か。 問題 11 このプログラムで使用しているレイアウトファイルのファイル名を書け。 (2)レイアウトファイル main.xml の編集 レイアウトファイルは以下とする。左端の「:」までは行番号 1: <?xml version="1.0" encoding="utf-8"?> 2: <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3: android:layout_width="fill_parent" android:layout_height="fill_parent"> 4: <view class="jp.ac.meisei.johojikken2.JikkenView" 5: android:layout_width="wrap_content" 6: android:layout_height="wrap_content" /> 7: <!-- 8: <view class="jp.ac.meisei.johojikken2.JikkenSurfaceView" 9: android:layout_width="wrap_content" 10: android:layout_height="wrap_content" /> 11: --> 12: <TextView android:layout_width="wrap_content" 13: android:layout_height="wrap_content" android:text="ボールゲーム" 14: android:textSize="20dip" android:layout_gravity="center" /> 15: </FrameLayout> コメントの開始タグは <!-- である。コメントの終了タグは --> である。 res ホルダーの中の layout ホルダーの中にある main.xml を上記のように編集する。 問題 12 上記のレイアウトファイルでコメントは何行から何行までか? 問題 13 上記のレイアウトファイルで FrameLayout タグの開始は何行か? FrameLayout タグの終了は何行か? FrameLayout クラスとは FrameLayout クラスはウィジェットをウィンドウに1つ配置するように設計されている。 レイアウトの一番左上が(0,0)という座標である。複数のウィジェット配置すると、

後に配置したウィジェットが 上段に表示される。上記例では JikkenView と TexiViewというウィジェットが配置される。 上記の例では、 後に配置されるのは TextView で、「ボールゲーム」という文字を表示す

る。文字の大きさが 20dip、文字を置く場所は layout_gravity 属性の値が「center」なので 縦、横について中央となる場所に配置する。 以下で表現できる。

115

Page 8: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

(3) JikkenView クラスにつて: JikkenView.java の編集 ここでは main.xml の 4 行で使用される JikkenView クラスを作成する。 画面上にボールが動くウィジェットである。ボールは画面の境界をバウンドする。ボー

ル同士が交叉した時は、交叉回数を1増やす。 View#onTouchEvent メソッドをオーバーライドして、画面のタッチ時に新しいボールを

追加する。以下が JikkenView.java のコードである。 1: package jp.ac.meisei.johojikken2; 2: import java.util.ArrayList; 3: import java.util.List; 4: import android.content.Context; 5: import android.graphics.Canvas; 6: import android.graphics.Color; 7: import android.graphics.Paint; 8: import android.graphics.Rect; 9: import android.graphics.drawable.Drawable; 10: import android.util.AttributeSet; 11: import android.view.MotionEvent; 12: import android.view.View;

13: public class JikkenView extends View { 14: private static final int MAX_COUNT = 130; 15: private List<Ball> balls = new ArrayList<Ball>(MAX_COUNT); 16: private CollisionComb<Ball> combination = new CollisionComb<Ball>( ); 17: private Drawable drawable; 18: int allBallsNum=10;// この値を 10,30,50 に変更して実験する 19: int kousaNum=0; int drawNum=0;// 描画回数 20: long time,kaishijikan,keikajikan; //時間 ミリ秒単位 21: public JikkenView(Context context) { 22: super(context); 23: initialize(context); 24: } 25: public JikkenView(Context context, AttributeSet attrs, int defStyle) { 26: super(context, attrs, defStyle); 27: initialize(context); 28: }

116

Page 9: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

29: public JikkenView(Context context, AttributeSet attrs) { 30: super(context, attrs); 31: initialize(context); 32: } 33: private void initialize(Context context) { 34: drawable = context.getResources().getDrawable(R.drawable.ball); 35: setBackgroundColor(Color.WHITE); 36: } 37: @Override 38: protected void onDraw(Canvas canvas) { 39: super.onDraw(canvas); 40: // 他のボールと交叉したときは、交叉回数を増やす。 41: for (List<Ball> pair : combination.getCombinations(balls)) 42: kousaNum+=pair.get(0).crossOfBalls(pair.get(1)); 43: // ボールの移動し、壁とぶつかったら向きを返す。 45: for (Ball ball : balls) 46: ball.move(); 47: // 描画 48: for (Ball ball : balls) 49: ball.draw(canvas); 50: drawNum++; 52: int count=balls.size(); // ボール数 52: Paint paint=new Paint(); 53: paint.setARGB(255,0,255,0); 54: Rect rect=new Rect(150,4,count+150,20); 55: canvas.drawRect(rect,paint); 56: String sCount=Integer.toString(count); 57: canvas.drawText("ボール数=", 2, 14, paint); 58: canvas.drawText(sCount, 80, 14, paint);//(4,14)から書く 59: String sDrawNum=Integer.toString(drawNum); 60: canvas.drawText("描画回数=", 2, 34, paint); 61: canvas.drawText(sDrawNum, 80, 34, paint);//(4,34)から書く 62: time=System.currentTimeMillis( ); 63: String sTime=Long.toString(time); 64: canvas.drawText("時間(現在、開始、経過、単位 ミリ秒)=", 2, 54, paint); 65: canvas.drawText(sTime, 10, 74, paint);//(4,54)から書く 66: if(count<=1 || count= =allBallsNum-1){kaishijikan=time;drawNum=0;} 67: String sKaishijikan=Long.toString(kaishijikan); 68: canvas.drawText(sKaishijikan, 100, 74, paint);//(100,54)から書く 69: keikajikan=time-kaishijikan; 70: String sKeikajikan=Long.toString(keikajikan); 71: canvas.drawText(sKeikajikan, 200, 74, paint);//(200,54)から書く 72: String sKousaNum=Integer.toString(kousaNum); 73: canvas.drawText("交叉回数=", 2, 94, paint); 74: canvas.drawText(sKousaNum, 80, 94, paint);

117

Page 10: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

75: if(keikajikan<50000)invalidate( );//経過時間が 50 秒超えると描画を終了 76: } 77: @Override 78: public boolean onTouchEvent(MotionEvent event) { 79: if(event.getAction( ) != MotionEvent.ACTION_DOWN 80: || balls.size( ) >= MAX_COUNT) 81: return false; 82: balls.add(new Ball(drawable, new Rect(getLeft( ), getTop( ), 83: getRight( ),:getBottom( )), (int) event.getX( ), (int) event.getY( ))); 84: ballsNum++; 85: return true; 86: } 87: } 38 行から onDraw メソッドをオーバーライドして、ボールを描画する処理を実装する。

1 ページ分をキャンバス(canvas)に描画する。キャンバスは画像を描画するオブジェクトで

ある。75 行で経過時間が 50 秒以内の場合 invalidate( )メソッドを呼び出すことで、onDrawメソッドが再度呼び出される。その結果、処理が繰り返される。 78 行からの onTouchEvent( )メソッドで、画面がタッチされたとき、新しいボールをタ

ッチした位置に追加する処理を行っている。 50 行から 74 行でボール数、描画回数、経過時間、ボールの交叉数を表示する。 52,53,54 行ではボール数に比例した長方形を描いている。 53 行の serARGB( )メソッドの説明 public void setARGB (int a, int r, int g, int b)

a:アルファ値 0 透明、255 不透明、r:red 値、g:green 値、b:blue 値 RGB(赤緑青)の数値を指定して色を作成、値は 0 から 255 の範囲

下記では不透明、緑 を指定。 53: paint.setARGB(255,0,255,0); 66 行で allBallsNum が実験するボール数、それより 1 個少ないボール数になったとき、 描画回数、開始時間を設定して、50 秒間描画している。 62 行の System.currentTimeMillis( )メソッドで、1971 年 1 月 1 日から現在までの経過時

間をミリ秒で取得する。75 行で経過時間が 50 秒を超えると、描画を停止している。 下記がそのスクリーンショットの画面である。

118

Page 11: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

(4)Ball クラスについて: Ball.java の編集 1: package jp.ac.meisei.johojikken2; 2: import java.util.Random; 3: import android.graphics.Canvas; 4: import android.graphics.Rect; 5: import android.graphics.drawable.Drawable; 6: public class Ball { 7: private int width; 8: private int height; 9: private Rect viewRect; 10: Drawable drawable; 11: Rect rect; 12: Direction direction; boolean crossFlag,oldCrossFlag; 13: public Ball(Drawable drawable, Rect viewRect, int x, int y) { 14: this.drawable = drawable; 15: this.width = drawable.getIntrinsicWidth( ) / 2; 16: this.height = drawable.getIntrinsicHeight( ) / 2; 17: this.rect = new Rect(0, 0, width, height); 18: this.viewRect = viewRect; 19: rect.offset(x, y); 20: Random random = new Random( ); 21: direction = new Direction(random.nextInt(5) + 1, random.nextInt(5) + 1); 22: crossFlag=false; 23: oldCrossFlag=false; 24: }

119

Page 12: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

25: public void move( ) { 26: rect.offset(direction.dx, direction.dy); 27: if (rect.left < 0) { 28: direction.dx = -direction.dx; 29: rect.left = 0; 30: rect.right = rect.left + width; 31: } else if (rect.top < 0) { 32: direction.dy = -direction.dy; 33: rect.top = 0; 34: rect.bottom = rect.top + height; 35: } else if (rect.bottom > viewRect.bottom) { 36: direction.dy = -direction.dy; 37: rect.bottom = viewRect.bottom; 38: rect.top = rect.bottom - height; 39: } else if (rect.right > viewRect.right) { 40: direction.dx = -direction.dx; 41: rect.right = viewRect.right; 42: rect.left = rect.right - width; 43: } 44: } 45: public void draw(Canvas canvas) { 46: drawable.setBounds(rect); 47: drawable.draw(canvas); 48: } 49: public int crossOfBalls(Ball other) { 50: int center_1_x = width / 2 + rect.left; 51: int center_1_y = width / 2 + rect.top; 52: int center_2_x = other.width / 2 + other.rect.left; 53: int center_2_y = other.width / 2 + other.rect.top; 54: int diff_x = Math.abs(center_1_x - center_2_x); 55: int diff_y = Math.abs(center_1_y - center_2_y); 56: double diff = diff_x + diff_y; 57: if (diff <= width) { 58: this.crossFlag=true; other.crossFlag=true; if(this.oldCrossFlag==true&&other.oldCrossFlag==true) return 0; else return 1; 59: } 58: return 0; 60: } 61: class Direction { 62: private int dx; 63: private int dy;

120

Page 13: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

64: private Direction(int dx, int dy) { 65: this.dx = dx; 66: this.dy = dy; 67: } 68: } 69: } Ball クラスには内部クラス Direction がある。このクラスのオブジェクトは位置(x、y)

と方向(dx、dy)の値を持つ。これは描画を更新する毎にボールの位置を変えるとき

に使う値である。ボールの速度といえる。 17 行で、Ball を描く長方形の領域のオブジェクトを作っている。これは Rect クラスのオ

ブジェクトである。 Rect クラスについて

Rect クラスのコンストラクタ: public Rect (int left, int top, int right, int bottom)

左上(left,top)、右下(right,bottom)の四角形

19行 rect.offset(x,y); は、上記の4角形の領域の左上端の座標が(x,y)であることを表す。 20,21 行でボールの動く方向は乱数で作られることが分かる。 move( ) メソッドはボールが壁に衝突したとり、跳ね返る処理をしている。 draw( ) メソッドは描画対象のボールを drawable とし、描画するメソッドである。

JikkenView クラスの 34 行で drawable を設定している。 crossOfBalls( ) メソッドはボールが交叉する場合、「1」を、そうでないとき「0」を返す。 このプログラムでは交叉の総数を求め表示する。 (5)CollisionComb クラスについて: CollisionComb.java の編集 以下がプログラムリスト 1: package jp.ac.meisei.johojikken2;

2: import java.util.ArrayList;

3: import java.util.Collections;

4: import java.util.List;

5: public class CollisionComb<T> {

6: private int itemSize = -1;

7: private int combinationSize = 2;

8: private List<List<T>> storedCombinationItems = Collections.emptyList();

9: public List<List<T>> getCombinations(List<T> items) {

10: if (itemSize == items.size())

左図で①、②、③、④は以下の直線 ① x=left, ② y=top, ③ x=right、④ y=bottom

121

Page 14: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

11: return storedCombinationItems;

12: itemSize = items.size();

13: storedCombinationItems = getCombinationsImpl(items, combinationSize);

14: return storedCombinationItems;

15: }

16: private List<List<T>> getCombinationsImpl(List<T> items, int size) {

17: int itemSize = items.size();

18: List<List<T>> result = new ArrayList<List<T>>();

19: List<T> pair;

20: for(int itemN=0;itemN<itemSize-1;itemN++) {

21: for (int item2 = itemN+1; item2 < itemSize; item2++) {

22: pair = new ArrayList<T>();

23: pair.add(items.get(itemN));

24: pair.add(items.get(item2));

25: result.add(pair);

26: }

27: }

28: return result;

29: }

30: }

CollisionComb クラスについて <T> をジェネリクス generics という。 8 行:このクラスはジェネリクス<T>を使っている。オブジェクトを作る時、この T には クラス名を指定する。JikkenView クラスの 16 行から、Tは Ball クラスが置かれて いることが分かる。以後はTを Ball と読み替えてプログラムを読むこと。 このクラスのメソッドは以下の2つである。 9 行から getCombinations( )メソッド これを使っている JikkenView クラスの 16 行を見ると、引数はボールのリストである

ことが分かる。 16 行から getCombinationsImpl( )メソッド

9 行で、getCombinations( )メソッドの引数は List<T> items となっている。このメ ソッドを使っているのは JikkenView クラスの 41 行である。そこでは引数 balls ですべ てのボールを List にして実引数として使っている。 従って items は Ball のオブジェクトのリストである。n個のボールを ball0,ball1,… ball(n-1)とすると、(ball0,ball1,…,ball(n-1))のようなデータを想像すること。 8 行で、storedCombItems は List<List<T>> 型 となっているので、これは Ball オブ ジェクトのリストのリストである。Ball オブジェクトを ball0…ball(n-1)とすると、 ((ball0,ball1),…,(ballx,bally),…(ballk,ball(n-1)))のようなデータを想像すること。

10,11 行から、items.size( )の値が「-1」なら、空のリストが戻り値となる。 13 行: getCombinationsImpl( )メソッドを実行してその結果を戻り値とする。

122

Page 15: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

16 行: getCombinationsImpl( )メソッドの定義 第 1 引数はボールのリスト、第 2 引数は「2」である。 17 行:ボール数を itemSize に置く。 18 行:result はボールのリストのリスト。(ボールの対のリスト) 22 行から 25 行 1 組のボールの対を作る。それが pair に置かれる。pair を result に加える。 (6) menu 関係の部分 :これは実験時間に余裕のある学生が行ってください。 JikkenActivity.java の 10 行に加える「menu」のプログラム @Override

public boolean onCreateOptionsMenu(Menu menu){

super.onCreateOptionsMenu(menu);

MenuInflater inflater = getMenuInflater();

inflater.inflate(R.menu.menu, menu);

return true;

}

@Override

public boolean onOptionsItemSelected(MenuItem item){

switch(item.getItemId()){

case R.id.menu_A:

finish();

return true;

}

return false;

} これを挿入することにより必要なインポート文の挿入作業は自分で行うこと menu.xml の作成 res ホルダに menu ホルダを作り、そこに「menu.xml」というファイルを作る。 そのコードは以下である。 <?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@+id/menu_A"

android:title="end of Jikken"

/>

</menu> 終了方法:このプログラムを動かし、ボールが(allBallsNum-1)個になった後、50 秒経過す

ると描画を終了する。その時点でメニューからアクテビティを終了させる。 レポートに記述すること 以下について記述すること。 ① 実験の目的を書く 第一部でのレポート ② 問題1から問題 13 までの解答を書く

123

Page 16: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

③ ボール 10 個、30 個、50 個を 50 秒間動かして描画回数を比較する。 表と図(横軸 ボール数、縦軸 描画回数)で結果を書く。

④ ボール 10 個になったときから計測を始めて 50 秒間で ボールをさらに 20 個作る(合計 30 個)場合、 ボール 30 個になったときから計測を始めて 50 秒間で ボールをさらに 20 個作る(合計 50 個)場合、 の描画回数を表で比較する。③の 30 個、50 個の場合と比較する。

⑤ プログラム作成問題 赤い長方形の箱を画面の上から下に移動する。下に達したら再度上から下に移動 する。これを繰り返す Android アプリを作りなさい。 そのプログラムの JikkenView.java のプログラムを書きなさい。 SurfaceView のウィジェットで実験を行った者は下記について報告する。 ⑥ ボール 10 個、30 個、50 個を 50 秒間動かして描画回数を比較する。 表と図(横軸 ボール数、縦軸 描画回数)で結果を書く。 ③の結果と比較する。

⑦ ボール 10 個になったときから計測を始めて 50 秒間で ボールをさらに 20 個作る(合計 30 個)場合、 ボール 30 個になったときから計測を始めて 50 秒間で ボールをさらに 20 個作る(合計 50 個)場合、 の描画回数を表で比較する。問題 3 の 30 個、50 個の場合と比較する。 ④の結果と比較する。 ⑧ ③、④の結果から、または③から⑥までの結果から、この場合の描画性能につい て何が言えるかを書きなさい。 第二部でのレポート 独自の View を利用したソートについて下記を報告する。

⑨ Android 実験で行ったソート方法はどのようなソート方法であったか、ネット等を利

用し調べ報告してください。 ⑩ Android 実験で行ったソート方法以外にどのようなソート方法があるか、ネット等を

利用し調べ報告してください。 ⑪ 調べたソート方法の一つを採用し、実験で作成したプログラムをそのソート方法で

改造し、ソースコードを報告してください。 ⑫ ネット等を利用しサウンドについて調べ地雷ゲームでゲームオーバーになるときサ

ウンドがなるように改造し、ソースコードを報告してください。 参考資料 この資料のプログラムと実際動作させたプログラムに違いがある可能性がある

ので、動作したプログラム JikkenView.java、Ball.java のリストを以下につける。 package jp.ac.meisei.johojikken2;

import 文は省略

public class JikkenView extends View {

private static final int MAX_COUNT = 130;

private List<Ball> balls

= new ArrayList<Ball>(MAX_COUNT);

private CollisionComb<Ball> combination =

new CollisionComb<Ball>();

private Drawable drawable;

int allBallsNum=10;

long time,kaishijikan,keikajikan; //時間ミリ秒

int kousaNum=0; int drawNum=0;// 描画回数

124

Page 17: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

public JikkenView(Context context) {

super(context);

initialize(context);

}

public JikkenView(Context context,

AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

initialize(context);

}

public JikkenView(Context context,

AttributeSet attrs) {

super(context, attrs);

initialize(context);

}

private void initialize(Context context) {

drawable=context.getResources()

.getDrawable(R.drawable.ball);

setBackgroundColor(Color.WHITE);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

// 他のボールと交叉したとき、kousaNum を1増やす。

for(List<Ball>pair:

combination .getCombinations(balls))

kousaNum=

kousaNum+pair.get(0).crossOfBalls(pair.get(1));

// ボールの移動し、壁とぶつかったら向きを返す。

for (Ball ball : balls)

ball.move();

// 描画

for (Ball ball : balls)

ball.draw(canvas);

drawNum++;

int count=balls.size(); // ボール数

Paint paint=new Paint();

paint.setARGB(255,0,255,0);

Rect rect=new Rect(150,4,count+150,20);

canvas.drawRect(rect,paint);

String sCount=Integer.toString(count);

canvas.drawText("ボール数=", 2, 14, paint);

canvas.drawText(sCount, 80, 14, paint);//(4,14)から

String sDrawNum=

Integer.toString(drawNum);

canvas.drawText("描画回数=", 2, 34, paint);

canvas.drawText(sDrawNum, 80, 34, paint);

//(4,34)から書く

time=System.currentTimeMillis();

String sTime=Long.toString(time);

canvas.drawText("時間(現在、開始、経過、単位 ミ

リ秒)=", 2, 54, paint);

canvas.drawText(sTime, 10, 74, paint);//(4,54)から

if(count<=0 || count==allBallsNum-1)

{kaishijikan=time;drawNum=0;}

String sKaishijikan= Long.toString(kaishijikan);

canvas.drawText(sKaishijikan, 100, 74, paint);

keikajikan=time-kaishijikan;

String sKeikajikan=Long.toString(keikajikan);

canvas.drawText(sKeikajikan, 200, 74, paint);

String sKousaNum=Integer.toString(kousaNum);

canvas.drawText("交叉回数=", 2, 94, paint);

canvas.drawText(sKousaNum, 80, 94, paint);

if(keikajikan<50000)invalidate();

//経過時間が 50 秒超えると描画を終了

}

@Override

public boolean onTouchEvent(MotionEvent event) {

if (event.getAction( ) !=

MotionEvent.ACTION_DOWN

|| balls.size() >= MAX_COUNT)

return false;

balls.add(new Ball(drawable,

new Rect(getLeft( ), getTop( ),

getRight(), getBottom()),(int) event.getX(),

(int) event.getY()));

return true;}

}

Ball.java のコード

package jp.ac.meisei.johojikken2;

import 文は省略

public class Ball {

private int width;

private int height;

private Rect viewRect;

Drawable drawable;

Rect rect;

Direction direction;

boolean crossFlag,oldCrossFlag;

public Ball(Drawable drawable, Rect viewRect, int

x, int y) {

125

Page 18: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

this.drawable = drawable;

this.width = drawable.getIntrinsicWidth() / 2;

this.height = drawable.getIntrinsicHeight() / 2;

this.rect = new Rect(0, 0, width, height);

this.viewRect = viewRect;

rect.offset(x, y);

Random random = new Random();

direction = new Direction(random.nextInt(5)+1,

random.nextInt(5) + 1);

crossFlag=false;

oldCrossFlag=false;

}

public void move() {

rect.offset(direction.dx, direction.dy);

if (rect.left < 0) {

direction.dx = -direction.dx;

rect.left = 0;

rect.right = rect.left + width;

} else if (rect.top < 0) {

direction.dy = -direction.dy;

rect.top = 0;

rect.bottom = rect.top + height;

} else if (rect.bottom > viewRect.bottom)

{

direction.dy = -direction.dy;

rect.bottom = viewRect.bottom;

rect.top = rect.bottom - height;

} else if (rect.right > viewRect.right) {

direction.dx = -direction.dx;

rect.right = viewRect.right;

rect.left = rect.right - width;

}

}

public void draw(Canvas canvas) {

drawable.setBounds(rect);

drawable.draw(canvas);

}

public int crossOfBalls(Ball other) {

int center_1_x = width / 2 + rect.left;

int center_1_y = width / 2 + rect.top;

int center_2_x = other.width / 2 +

other.rect.left;

int center_2_y = other.width / 2 +

other.rect.top;

int diff_x = Math.abs(center_1_x -

center_2_x);

int diff_y = Math.abs(center_1_y -

center_2_y);

double diff = diff_x + diff_y;

if (diff <= width) {

this.crossFlag=true;

other.crossFlag=true;

if(this.oldCrossFlag==true

&&other.oldCrossFlag==true)return 0;

else return 1;

} return 0;

}

class Direction {

private int dx;

private int dy;

private Direction(int dx, int dy) {

this.dx = dx;

this.dy = dy;

}

}

}

126

Page 19: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

3. SurfaceView クラスを使ったプログラム 3.1 SurfaceView を利用するプログラムの概要 ここに SurfaceView クラスの説明資料を付すが、時間的に実験をするのは難しいと 考えられるので、時間に余裕があり実験できる学生は自分で行い、実験報告書にそれを報

告すること。 View クラスを継承した場合、描画処理を View#onDraw メソッドで行う。これを行うス

レッドをメインスレッドという。このメインスレッドでは画面のタッチイベントである View#onTouchEvent も処理される。描画処理と他の処理をあわせて行う。それに対して SurfaceView を利用するとメインスレッドとは別なスレッドを描画専用として割り当てら

れる。 このアプリケーションの重要点 ① SurfaceView クラスを継承したカスタムウィジェットの理解 ② Thread の理解 SurfaceView を利用するプログラムの構成(要点) 省略 public class JikkenSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable { 省略 private Thread thread; コンストラクタなども省略 (以下で Thread を起動) @Override public void surfaceCreated(SurfaceHolder holder) { thread = new Thread(this); thread.start( ); } (以下で Thread を終了) @Override public void surfaceDestroyed(SurfaceHolder holder) { thread = null; } (描画を行うの Thread のプログラム部分の canvas を生成、描画後 unlock する部分) @Override public void run( ) { 省略 以下で Canvas クラスのオブジェクト生成、lock で描画は保留される canvas = holder.lockCanvas( ); 描画 以下で描画したものを表示、unlock で描画が実施される holder.unlockCanvasAndPost(canvas);

127

Page 20: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

} 下図のように2つの処理が並行して行われる。

@Override について 右記より引用 http://www.techscore.com/tech/J2SE/JavaLanguage/7.html

Java 言語機能(JDK5.0(Tiger)新機能) アノテーション

J2SE5.0 ではプログラムの動作に影響を与えないアノテーション(注釈)を付加すること

ができる。アノテーションがコメントと異なるのは、プログラムからアノテー ションを読

み取って処理したりできる点や、アノテーションがコンパイラの動作に影響を与える点な

どである。

標準アノテーション型

J2SE5.0 であらかじめ以下の標準アノテーション型が用意されています。

Override

Deprecated

SuppressWarnings

Override アノテーション

Override アノテーションは、メソッドにつけることのできるアノテーション。

Override アノテーションはメソッドがオーバーライドしていることを宣言する。

以下に Override アノテーションの例を示す。アノテーションはアノテーション型名の前

に「@」を付けて使用する。

Class A {

public void a() {}

}

Class B extends A {

@Override public void a() {}

}

オーバーライドされていないメソッドに Override アノテーションを付けているとコンパ

イルエラーとなりる。オーバーロード(シグネチャの異なる同名メソッドの定義)でもエ

ラーとなる。必ずオーバー ライドしていなければならない。このことにより、タイプミス

などによるメソッド名や引数の型の間違いを防止することができる。

以下のプログラムは 1 章のものと同じである。 JikkenActivity.java Ball.java

128

Page 21: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

CollisionComb.java レイアウトファイルは以下とする。左端の「:」までは行番号 1: <?xml version="1.0" encoding="utf-8"?> 2: <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3: android:layout_width="fill_parent" android:layout_height="fill_parent"> 4: <!-- 5: <view class="jp.ac.meisei.johojikken2.JikkenView" 6: android:layout_width="wrap_content" 7: android:layout_height="wrap_content" /> 8: --> 9: <view class="jp.ac.meisei.johojikken2.JikkenSurfaceView" 10: android:layout_width="wrap_content" 11: android:layout_height="wrap_content" /> 12: <TextView android:layout_width="wrap_content" 13: android:layout_height="wrap_content" android:text="ボールゲーム" 14: android:textSize="20dip" android:layout_gravity="center" /> 15: </FrameLayout> 3.2 JikkenSurfaceView の概要 JikkenSurfaceView のコンストラクタが 3つある。 第1引数 Context クラス変数、 第2引数 AttributeSet クラス変数、 第 3 引数 int 型 カスタムウィジェットを作る場合には、第 1 引数だけのコンストラクタ、 第 1 引数、第2引数のコンストラクタ、第 1 引数から第3引数のコンストラクタ の3つがある。これは同じ引数のコンストラクタが View クラスにあることに対応する。 各コンストラクタは initialize( )メソッドを実行している。引数は Context クラス変数。 このメソッドの処理内容 ①getHolder( )メソッドで SurfaceHolder クラスのオブジェクトを取り出し、 holder に代入。45 行 ② holder.addCallBack(this); 46 行 SurfaceHolder.Callback インターフェイスには、SurfaceView の起動時や破棄時に呼び出

されるコールバックメソッドを定義できる。このコールバックを SurfaceView.addCallbackメソッドを使い、登録。 (英文:The Callback is set with SurfaceHolder.addCallback method.) ③ Context 変数を使ってリソース(res)を作り、ボールの Drawable オブジェク ト drawable を作っている。 48 行 ④ SurfaceHolder クラスの setFixedSize ( ) メソッドについて 47 行 固定サイズの surface を作るのに利用 ⑤ View クラスの getHeight( ),getWidth( )メソッドについて 47 行 ビューの高さ、幅を返す。 Thread の開始は surfaceCreated( ) メソッドで行っている。56 行 58 行で Thread クラスのコンストラクタが起動、

129

Page 22: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

58: thread = new Thread(this); 引数 this(このクラスのオブジェクト)は Runnable インターフェースのオブジェクトにな

っている。以下の形のコンストラクタが使われている。 Thread(Runnable runnable) これは Runnable インターフェースを実装した オブジェクトを引数とする新しい Thread

オブジェクトを作る。

コールバックメソッドには、SurfaceView クラスのコールバックと SurfaceHolder.Callback インターフェースのコールバックがある。 コールバックメソッド ① surfaceChanged( ) ② surfaceCreated( ) ③ surfaceDestroyed( ) SurfaceHolder.Callback インターフェースを implements すると、上記の3つのメソッド

を実装することが必要 ④ onTouchEvent( ) onTouchEvent メソッドを、SurfaceView クラスを継承した JikkenSurfaceView クラス内

で Override する。すると、SurfaceView 上でタッチした場合に、そのイベントを拾えるよ

うになる。 3.3 run( )メソッドについて

Runnable インターフェースを実装すると、この run( )メソッドを実装することが必要と

なる。 イインンタターーフフェェーースス RRuunnnnaabbllee のの説説明明かからら public interface Runnable

インスタンスを 1 つのスレッドで実行するすべてのクラスでは、Runnable インタフェー

スを実装する必要があります。このクラスは、引数のないメソッド run を定義しなければ

なりません。

このインタフェースは、アクティブな間にコードを実行したいオブジェクトが使う、共通

のプロトコルを提供するために設計されています。たとえば、Runnable は Thread クラス

によって実装されます。アクティブであるということは、スレッドが開始されて、まだ終

了していない状態を意味します。( start( )メソッドが実行されたとき )

さらに Runnable は、Thread をサブクラス化せずにクラスをアクティブにする手段を提供

します。Runnable を実装するクラスは、Thread のインスタンスを生成し、ターゲットと

してクラス自身を渡すことにより Thread をサブクラス化をしなくても実行できます。

Thread クラスのメソッドのうち、run() だけをオーバーライドして使用する場合は、

Runnable インタフェースを使用してください。これは、クラスの基本的な動作を修正また

は拡張するのでない限り、そのクラスをサブクラス化することは好ましくないため、重要

です。

130

Page 23: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

その他の情報 android でサブスレッドを起動したい場合には AsyncTask という使い勝手のいいクラス

がありますが、元々java 経験のある人はオレは今まで通り Runnable,Thread を使ってマル

チスレッドプログラミングするぜ!って人がいるかもしれません。

http://www.techmaru.net/wordpress/20110203/subthreadasynctask/

3.4 SurfaceView について SurfaceView が実行すると、途中で run( )メソッドでメインスレッドとは別なスレッドが 動作する。メインスレッドではコールバック処理などを行う。別なスレッドでは描画処理

を行う。 JikkenSurfaceActivity クラス run( )メソッドの 80 行、108 行で lockCanvas メソッド、

unlockCanvasAndPost メソッドを使用している。unlockCanvasAndPost メソッドが実行

されると描画が画面に表示される。 コールバックについて SurfaceHolder のオブジェクト holder のメソッド addCallBack(this) を実行する。 これで SurfaceHolder にイベントが発生すると、SurfaceView のコールバックメソッドが 呼ばれる。 80 行: holder.lockCanvas( )メソッドで、canvas を取り出している。 「lock」という用語で、これは排他的に使用する Canvas と想像できる。 ボールの描画は Canvas クラスのオブジェクト canvas を引数に、Ball クラスの draw( ) メソッド(88 行)を実行して行う。 描画処理での注意 以下は右記 URL からの引用:http://d.hatena.ne.jp/shuji_w6e/20090127/1233070723 SurfaceView には draw というメソッドがありますが、このメソッドをオーバーライドし

ただけでは何も描画されません。というのも、Android ではリソースを節約するためなのか、

アプリケーションから描画を求めなければ描画されないようです。その為、キーイベント

やメインループなどをトリガーとして、描画メソッドを呼び出します。 描画メソッドは draw をオーバーライドしなくても構いませんが、描画に必要なのは

android. graphics.Canvas のインスタンスになります。この Canvas のインスタンスを取得

するためには、SurfaceHolder の lockCanvas メソッドを使います。lockCanvas 名前から

推測できるように、マルチスレッド環境を想定した作りになっています。あるスレッドが

描画処理を行っている間、他のスレッドからの描画は排他します。したがって、描画後に

必ず unlockCanvasAndPost メソッドを呼び出す必要があります。尚、SurfaceHolder は

SurfaceView の getHolder メソッドから参照できます。 3.5 JikkenSurfaceView.java のコード(行番号をつける) スレッドによる並列処理に関係あるコードとして以下の2つがある。 23: private List<Ball> balls 24: = Collections.synchronizedList(new ArrayList<Ball>(MAX_COUNT)); ドキュメントでは以下の説明になっている。 Returns a wrapper on the specified List which synchronizes all access to the List balls というリストにアクセスすら場合は、すべて排他処理で行われる宣言である。

131

Page 24: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

84: synchronized (balls) { …… 95: } 84 行から 95 行は、オブジェクト balls については排他処理を行うことを表わす。 すなわちこのループ実行中には、コールバックメソッドで balls を変更できない。 1: package jp.ac.meisei.johojikken2; 2: import java.util.ArrayList; 3: import java.util.Collections; 4: import java.util.List; 5: import android.content.Context; 6: import android.content.res.Resources; 7: import android.graphics.Bitmap; 8: import android.graphics.BitmapFactory; 9: import android.graphics.Canvas; 10: import android.graphics.Color; 11: import android.graphics.Paint; 12: import android.graphics.Rect; 13: import android.graphics.RectF; 14: import android.graphics.drawable.Drawable; 15: import android.util.AttributeSet; 16: import android.view.MotionEvent; 17: import android.view.SurfaceHolder; 18: import android.view.SurfaceView; 19: public class JikkenSurfaceView extends SurfaceView 20: implements SurfaceHolder.Callback, Runnable { 21: private SurfaceHolder holder; 22: private static final int MAX_COUNT = 130; 23: private List<Ball> balls 24: = Collections.synchronizedList(new ArrayList<Ball>(MAX_COUNT)); 25: private CollisionComb<Ball> combination 26: = new CollisionComb<Ball>( ); 27: private Drawable drawable; 28: private Bitmap mBackgroundImage; 29: //ScheduledExecutorService executor; 30: private Thread thread; 31: int kousaNum=0;int allBallsNum=10; 32: long time,kaishijikan=0L,keikajikan; //時間 ミリ秒単位

33: int drawNum=0;// 描画回数

34: public JikkenSurfaceView(Context context) { 35: super(context);

132

Page 25: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

36: initialize(context); 37: } 38: public JikkenSurfaceView(Context context, AttributeSet attrs, int defStyle) { 39: super(context, attrs, defStyle); 40: initialize(context); 41: } 42: public JikkenSurfaceView(Context context, AttributeSet attrs) { 43: super(context, attrs); 44: initialize(context); 45: } 46: private void initialize(Context context) { 47: holder = getHolder(); 48: holder.addCallback(this); 49: holder.setFixedSize(getWidth(), getHeight()); 50: drawable = context.getResources().getDrawable(R.drawable.ball); 51: } 52: @Override 53: public void surfaceChanged(SurfaceHolder holder, 54: int format, int width, int height) { 55: } 56: @Override 57: public void surfaceCreated(SurfaceHolder holder) { 58: thread = new Thread(this); 59: thread.start(); 60: } 61: @Override 62: public void surfaceDestroyed(SurfaceHolder holder) { 63: thread = null; 64: } 65: @Override 66: public boolean onTouchEvent(MotionEvent event) { 67: if (event.getAction() != MotionEvent.ACTION_DOWN 68: || balls.size() >= MAX_COUNT) return false; 69: balls.add(new Ball(drawable, new Rect(getLeft( ), getTop( ), getRight( ), 70: getBottom( )),(int) event.getX( ), (int) event.getY( ))); 71: return true; 72: }

133

Page 26: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

73: @Override 74: public void run() { 75: long time; 76: int drawNum=0; 77: Canvas canvas = null; 78: Paint p = new Paint(); 79: p.setColor(Color.WHITE); 80: while (thread != null && keikajikan<50000) {

//経過時間が 50 秒超えると描画を終了 81: try { 82: canvas = holder.lockCanvas( ); 83: canvas.drawRect(0, 0, getWidth( ), getHeight( ), p); 84: synchronized (balls) { 85: // 他のボールとの交叉数 86: for (List<Ball> pair : combination.getCombinations(balls)) 87: crossNum += pair.get(0).crossOfBalls(pair.get(1)); 88: // 壁との衝突 + ボールの移動 89: for (Ball ball : balls) 90: ball.move(); 91: for (Ball ball : balls) //描画開始

92: ball.draw(canvas);

93: drawNum++;

94: int count=balls.size( );

95: }// synchronized ブロック終了

96: Paint paint=new Paint();

97: paint.setARGB(255,0,255,0);

98: Rect rect=new Rect(150,4,count+150,20);

99: canvas.drawRect(rect,paint);

100: String sCount=Integer.toString(count);

101: canvas.drawText("ボール数=", 2, 14, paint);

102: canvas.drawText(sCount, 80, 14, paint);

103: String sDrawNum=Integer.toString(drawNum);

104: canvas.drawText("描画回数=", 2, 34, paint);

105: canvas.drawText(sDrawNum, 80, 34,paint);

106: time=System.currentTimeMillis();

107: String sTime=Long.toString(time);

108: canvas.drawText("時間(現在、開始、経過、単位 ミリ秒)=", 2, 54, paint);

109: canvas.drawText(sTime, 10, 74, paint);

110: if(count<=0 || count==allBallsNum-1){kaishijikan=time;drawNum=0;}

111: String sKaishijikan=Long.toString(kaishijikan);

112: canvas.drawText(sKaishijikan, 100, 74, paint);

113: keikajikan=time-kaishijikan;

114: String sKeikajikan=Long.toString(keikajikan);

115: canvas.drawText(sKeikajikan, 200, 74, paint);

116: String sKousaNum=Integer.toString(kousaNum);

134

Page 27: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

117: canvas.drawText("交叉回数=", 2, 94, paint);

118: canvas.drawText(sKousaNum, 80, 94, paint);

120: } finally { 121: if (canvas != null) 122 holder.unlockCanvasAndPost(canvas); 123: } 124: } 125: } 126: } menu 関係の部分 :これは実験時間に余裕のある学生が行ってください。 JikkenView の memu の説明を参考にメニューを加える。 参考資料 この資料のプログラムと実際動作させたプログラムに違いがある可能性がある

ので、動作したプログラム JikkenSurfaceView.java のリスト(2 段)を以下につける。 package jp.ac.meisei.johojikken2;

import 文は省略

public class JikkenSurfaceView extends SurfaceView

implements SurfaceHolder.Callback, Runnable {

private SurfaceHolder holder;

private static final int MAX_COUNT = 130;

private List<Ball> balls=

Collections.synchronizedList(new

ArrayList<Ball>(MAX_COUNT));

private CollisionComb<Ball> combination=

new CollisionComb<Ball>( );

private Drawable drawable;

//ScheduledExecutorService executor;

private Thread thread;

int kousaNum=0; int allBallsNum=10;

long time,kaishijikan=0L,keikajikan;

///時間 ミリ秒単位

int drawNum=0;// 描画回数

public JikkenSurfaceView(Context context) {

super(context);

initialize(context);

}

public JikkenSurfaceView(Context context,

AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

initialize(context);

}

public JikkenSurfaceView(Context context,

AttributeSet attrs) {

super(context, attrs);

initialize(context);

}

private void initialize(Context context) {

holder = getHolder();

holder.addCallback(this);

holder.setFixedSize(getWidth(), getHeight());

drawable =

context.getResources().getDrawable(R.drawable.ball);

}

@Override

public void surfaceChanged(SurfaceHolder holder,

int format, int width, int height) {

}

@Override

public void surfaceCreated(SurfaceHolder holder) {

thread = new Thread(this);

thread.start();

}

@Override

public void surfaceDestroyed(SurfaceHolder holder)

{

thread = null;

}

@Override

public boolean onTouchEvent(MotionEvent event) {

if (event.getAction() !=

MotionEvent.ACTION_DOWN

135

Page 28: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

|| balls.size() >= MAX_COUNT)

return false;

balls.add(new Ball(drawable,

new Rect(getLeft( ), getTop( ), getRight( ),

getBottom( )),(int) event.getX( ),

(int) event.getY( )));

return true;

}

@Override

public void run() {

drawNum=0;

Canvas canvas = null;

Paint p = new Paint();

p.setColor(Color.WHITE);

while (thread != null && keikajikan<50000) {

//経過時間が 50 秒超えると描画を終了

try {

canvas = holder.lockCanvas( );

canvas.drawRect(0, 0, getWidth( ),

getHeight( ), p);

synchronized (balls) {

// 他のボールとの交叉回数を求める

for (List<Ball> pair :

combination.getCombinations(balls))

kousaNum

+=pair.get(0).crossOfBalls(pair.get(1));

// 壁との衝突 + ボールの移動

for (Ball ball : balls)

ball.move();

for (Ball ball : balls)

ball.draw(canvas);

// 描画

drawNum++;

int count=balls.size( );

}

Paint paint=new Paint();

paint.setARGB(255,0,255,0);

Rect rect=new Rect(150,4,count+150,20);

canvas.drawRect(rect,paint);

String sCount=Integer.toString(count);

canvas.drawText("ボール数=", 2, 14, paint);

canvas.drawText(sCount, 80, 14, paint);

String sDrawNum=

Integer.toString(drawNum);

canvas.drawText("描画回数=", 2, 34, paint);

canvas.drawText(sDrawNum, 80, 34,paint);

time=System.currentTimeMillis();

String sTime=Long.toString(time);

canvas.drawText("時間(現在、開始、経過、単位

ミリ秒)=", 2, 54, paint);

canvas.drawText(sTime, 10, 74, paint);

if(count<=0 || count==allBallsNum-1){

kaishijikan=time;drawNum=0;}

String sKaishijikan=Long.toString(kaishijikan);

canvas.drawText(sKaishijikan, 100, 74, paint);

keikajikan=time-kaishijikan;

String sKeikajikan=Long.toString(keikajikan);

canvas.drawText(sKeikajikan, 200, 74, paint);

String sKousaNum=

Integer.toString(kousaNum);

canvas.drawText("交叉回数=", 2, 94, paint);

canvas.drawText(sKousaNum, 80, 94, paint);

} finally {

if (canvas != null)

holder.unlockCanvasAndPost(canvas);

}

}

}

}

136

Page 29: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

第 2 部 独自の View を作ろう

4. ウォーミングアップ

4.1 画面の生成とイベントドリブン Android では XML を利用した画面生成方法と Java のみを利用した画面生成方法の 2 つ

の書き方があります。情報学実験Ⅰでは XML を利用した書き方を学びましたが、より高度

なプログラミングに対応するため、Java のみを利用した書き方を学びます。下記に簡単な

サンプルを記載します。 package ac.meisei_u.android.ButtonSample; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class ButtonSampleActivity extends Activity { Button btn; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // btn = (Button) findViewById(R.id.button1); btn = new Button(this); btn.setText("Hello"); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { btn.setText("World"); } }); setContentView(btn); // setContentView(R.layout.main); } } 本アプリと同じ動作をする res/layout/man.xml を用いた形で作成し、その違いを確認して

みましょう。

5. ソートを行うアプリケーションを作成しましょう ソートとは並び替えを行う方法です。小さい順に並べ替える方法を昇順、大きい順に並

べ替えることを降順と呼びます。プログラムでソートを行うには大小を比較して並べ替え

る対象を「選択」する、選択した値を「交換」する、交換を行うために「代入」するとい

137

Page 30: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

った、「選択」「交換」「代入」の概念が重要になります。 これらソートを行う方法は幾つかあり、アルゴリズムという概念で表されます。アルゴ

リズムとは「問題を解くためのものであって、明確に定義され、順序付けられた有限後の

規則からなる集合」と定義されます。 ここではいくつかのソートのアルゴリズムを学び、これを Android で表現してみます。

5.1 セレクションソート(選択ソート) セレクションソートは要素の先頭から順番に 大もしくは 小の値を探し、先頭の要素

と交換していく方法です。

まずは Java のプログラムでセレクションソートを実現してみましょう。 public class Sort1 {

public static void main(String [] args) {

int [] arr = {7,14,33,10,2,19,37,20};

int min, minIdx;

int i, j, tmp;

for (i = 0; i < arr.length-1; i++) {

min = arr[i];

minIdx = i;

for(j = i+1 ; j < arr.length; j++) {

if (arr[j] < min) {

min = arr[j];

138

Page 31: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

minIdx = j;

}

}

tmp = arr[minIdx];

arr[minIdx] = arr[i];

arr[i] = tmp;

}

for (i = 0; i < arr.length; i++) {

System.out.print(arr[i] + ",");

}

}

}

5.2 バブルソート バブルソートは隣接する要素同士を比較し、目的の順序になってなければその要素同時

を交換していく方法です。配列の末尾から要素が泡のように移動していくように見えるこ

とからバブルソートと呼ばれています。

public class Sort2 {

public static void main(String [] args) {

int [] arr = {7,14,33,10,2,19,37,20};

int i, j, tmp;

for (i = 0; i < arr.length; i++) {

for(j = arr.length-1 ; j > i; j--) {

if (arr[j] < arr[j-1]) {

tmp = arr[j];

139

Page 32: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

arr[j] = arr[j-1];

arr[j-1] = tmp;

}

}

}

for (i = 0; i < arr.length; i++) {

System.out.print(arr[i] + ",");

}

}

}

5.3 インサーションソート(挿入ソート) インサーションソートは先頭の配列をソート済みにしておき、後続の要素をソートされ

ていない部分に挿入していく方法になります。

public class Sort3 {

public static void main(String [] args) {

int [] arr = {7,14,33,10,2,19,37,20};

int i, j, tmp;

for (i = 1; i < arr.length; i++) {

140

Page 33: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

tmp = arr[i];

if (arr[i-1] > tmp) {

j = i;

do {

arr[j] = arr[j-1];

j--;

} while(j > 0 && arr[j-1] > tmp);

arr[j] = tmp;

}

}

for (i = 0; i < arr.length; i++) {

System.out.print(arr[i] + ",");

}

}

}

5.4 Android でソートプログラムを作成してみましょう Android の特性を生かし、見た目でソートが分かるように、ソートの見た目とソートを実

行する View を作成しましょう。まずはプロジェクトを作成し、新規 View を作成しましょ

う。以下がソースコードになります。 package ac.bbt.android.SortSample; import java.util.Random; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.View; public class SortView extends View { private static final int S = 300; private static int [] array = new int[300]; private static boolean isInit = false; private static int count = 0; /** * コンストラクタ * @param context */ public SortView(Context context) { super(context); } /** * 描画を行います

141

Page 34: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

*/ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); paint.setColor(Color.WHITE); for (int i = 0; i < S; i++) { canvas.drawLine(i, 0, i, array[i], paint); } } /** * 初期化を行います */ public void init() { Random random = new Random(); for (int i = 0; i < S; i++) { array[i] = random.nextInt(200); } count = 0; isInit = true; } /** * ソートを行います。 */ public void sort() { if (isInit && count+1 < S) { for(int i = count+1; i < S; i++) { if (array[count] > array[i]) { int t = array[count]; array[count] = array[i]; array[i] = t; } } count++; } } /** * 現在のカウント数を返却します。 */ public int getCount() { return count; } } View を継承し、onDraw()をオーバーライドし描画していることに注意しましょう。

Activity からこの SortView を呼び出してみましょう。Activity のコードは以下のとおりで

142

Page 35: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

す。 package ac.meisei_u.android.SortSample; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class SortSampleActivity extends Activity { private static Button initBtn; private static Button sortBtn; private static SortView sortView; private static TextView countText; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //インスタンス生成 initBtn = new Button(this); sortBtn = new Button(this); sortView = new SortView(this); countText = new TextView(this); //幅と高さ、文字列設定 initBtn.setWidth(ViewGroup.LayoutParams.FILL_PARENT); initBtn.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); sortBtn.setWidth(ViewGroup.LayoutParams.FILL_PARENT); sortBtn.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); countText.setWidth(80); countText.setHeight(40); initBtn.setText("初期化"); sortBtn.setText("整列"); //初期動作とイベント処理 sortView.init(); countText.setText(String.valueOf(sortView.getCount())); initBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sortView.init();

143

Page 36: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

sortView.invalidate(); countText.setText(String.valueOf(sortView.getCount())); } }); sortBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sortView.sort(); sortView.invalidate(); countText.setText(String.valueOf(sortView.getCount())); } }); //レイアウトに貼りつけ LayoutParams layoutParam = new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, 200); sortView.setLayoutParams(layoutParam); LinearLayout linearLayout = new LinearLayout(this); linearLayout.setOrientation(LinearLayout.VERTICAL); linearLayout.addView(sortView); linearLayout.addView(initBtn); linearLayout.addView(sortBtn); linearLayout.addView(countText); setContentView(linearLayout); } } 下記のように実行画面が表示され、整列ボタンを押すと整列していくか確認して下さい。

144

Page 37: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

6. 再帰処理を使ってコッホ曲線を描こう コッホ曲線とは、フラクタル図形の一つです。線分を 3 等分し、分割した 2 点を頂点と

する正三角形の作図を無限に繰り返すことによって得られる図です。

http://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%83%E3%83%9B%E6%9B%B2%E7%B7%9A Java でコッホ曲線を描くにはメソッドの再帰呼び出しを利用します。独自のコッホ曲線

を描く View を作成して Android でコッホ曲線を描いてみましょう。 package ac.meisei_u.android.KochSample; import java.util.Random; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.Log; import android.view.View; public class KochView extends View { private static int L = 1; private static double x1, x2, y1, y2; private static int iniLevel = 0; private static double iniAlpha = 0.0; /** * コンストラクタ * @param context */ public KochView(Context context) { super(context); } /** * 描画を行います

145

Page 38: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

*/ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); koch(canvas, iniLevel, iniAlpha); } /** * Koch 曲線を再帰的に描きます * @param canvas * @param level * @param alpha */ private void koch(Canvas canvas, int level, double alpha) { Paint paint = new Paint(); paint.setColor(Color.WHITE); if (level >= L) { x2 = x1 + 300 * Math.cos(alpha) / Math.pow(3, (double)level); y2 = y1 + 300 * Math.sin(alpha) / Math.pow(3, (double)level); canvas.drawLine((int)x1, (int)y1, (int)x2, (int)y2, paint); x1 = x2; y1 = y2; } else { double beta = alpha; level++; koch(canvas, level, beta); beta += Math.PI / 3; koch(canvas, level, beta); beta += Math.PI * 4 / 3; koch(canvas, level, beta); beta += Math.PI / 3; koch(canvas, level, beta); } } /** * 初期化を行います */ public void init() { x1 = 0; y1 = 10; iniLevel = 0; iniAlpha = 0.0; L = 1; } /** * 曲線のレベルを上げます

146

Page 39: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

*/ public void levelUp() { x1 = 0; y1 = 10; iniLevel = 0; iniAlpha = 0.0; L++; } /** * 現在のレベルを返却します。 */ public int getLevel() { return L; } } この View を使う Activity のサンプルは以下のとおりです。 package ac.meisei_u.android.KochSample; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class KochSampleActivity extends Activity { private static Button initBtn; private static Button levelUpBtn; private static KochView kochView; private static TextView countText; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //インスタンス生成 initBtn = new Button(this); levelUpBtn = new Button(this); kochView = new KochView(this); countText = new TextView(this);

147

Page 40: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

//幅と高さ、文字列設定 initBtn.setWidth(ViewGroup.LayoutParams.FILL_PARENT); initBtn.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); levelUpBtn.setWidth(ViewGroup.LayoutParams.FILL_PARENT); levelUpBtn.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); countText.setWidth(80); countText.setHeight(40); initBtn.setText("初期化"); levelUpBtn.setText("レベルアップ"); //初期動作とイベント処理 kochView.init(); countText.setText(String.valueOf(kochView.getLevel())); initBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { kochView.init(); kochView.invalidate(); countText.setText(String.valueOf(kochView.getLevel())); } }); levelUpBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { kochView.levelUp(); kochView.invalidate(); countText.setText(String.valueOf(kochView.getLevel())); } }); //レイアウトに貼りつけ LayoutParams layoutParam = new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, 200); kochView.setLayoutParams(layoutParam); LinearLayout linearLayout = new LinearLayout(this); linearLayout.setOrientation(LinearLayout.VERTICAL); linearLayout.addView(kochView); linearLayout.addView(initBtn); linearLayout.addView(levelUpBtn); linearLayout.addView(countText); setContentView(linearLayout); } } 実行し、レベルアップボタンを押すと下記のようにコッホ曲線が描かれます。

148

Page 41: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

同じような方法で樹木曲線を描いてみましょう。樹木曲線を描く View は以下のとおりです。

Activity はコッホ曲線と同じ形式とし、自分で作成してみましょう。 package ac.meisei_u.android.TreeSample;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.view.View;

public class TreeView extends View {

private static int L = 1;

/**

* コンストラクタ

* @param context

*/

public TreeView(Context context) {

super(context);

}

/**

* 描画を行います

*/

@Override

protected void onDraw(Canvas canvas) {

149

Page 42: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

super.onDraw(canvas);

tree(canvas, L, 150, 10, 50, 90);

}

/**

* 樹木曲線を再帰的に描きます

* @param canvas

* @param level

* @param alpha

*/

private void tree(Canvas canvas, int n, double x0, double y0, double len, double ang) {

Paint paint = new Paint();

paint.setColor(Color.WHITE);

double radian = Math.PI / 180.0;

double scale = 0.6;

int angle = 30;

int height = 200;

if (n <= 0) { return; }

// 枝の終点のx座標を計算

double x = len * Math.cos(radian * ang) + x0;

// 枝の終点のy座標を計算

double y = len * Math.sin(radian * ang) + y0;

// 始点終点が与えられた枝の描画

canvas.drawLine((int) x0, (int) (height - y0), (int) x, (int) (height - y), paint);

// 右側の枝の描画 (再帰呼び出し)

tree(canvas, n - 1, x, y, len * scale, ang - angle);

// 左側の枝の描画 (再帰呼び出し)

tree(canvas, n - 1, x, y, len * scale, ang + angle);

}

/**

* 初期化を行います

*/

public void init() {

L = 1;

}

/**

* 曲線のレベルを上げます

*/

public void levelUp() {

L++;

}

/**

* 現在のレベルを返却します。

*/

public int getLevel() {

return L;

150

Page 43: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

}

}

7. 地雷ゲームを作ってみましょう

ここでは皆さんもよく知っている地雷ゲームを作成してみようと思います。プログラムや画面の

大きな構造は以下のようになります。画面表示時には onCreate()が実行されます。メニューボ

タンを押したときは onCreateOptionsMenu()が実行されメニューが表示されます。メニューの

項目を選択したときは onMenuItemSelected()が実行されます。メニューから「盤面の設定」を

選択したときは設定画面が表示されます。画面をクリックした場合はや長押しした場合は、

onClick()や onLongClick()が呼び出されます。 この後、それぞれのメソッドを詳細に見ていきます。これら以外のメソッドは効率化のために

外だししたものです。 仕様は下記のとおりです。

1) 起動するとマス目が表示される 2) マス目をタップするとマス目が開く 3) 地雷をタップするとゲームオーバーとなり、経過時間と得点が表示される 4) 地雷でない場合は斜めを含む隣り合ったマスに何個地雷があるか表示する 5) ロングタップをすると旗を立てることができる 6) 爆弾の数とマス目の数はメニューから変更保存できるようにする 7) 全部パネルを開いたら、経過時間と得点を表示する

それでは作成していきましょう まずは元となる Android のプロジェクトを作成しましょう。「ファイルメニュー」→「新規」

→「Android プロジェクト」を選択し下記のように入力しましょう。 ・プロジェクト名: BombGame ・ビルドターゲット: Android 2.1-update1 ・アプリケーション名: 爆弾ゲーム ・パッケージ名: ac.meisei_u.android.BombGame ・Create Activity: BombGameActivity

プロジェクトを生成したら Part.1 と同じ要領で、BombGameActivity.java を開いてください。 Part.1 で述べたように Java ではクラスが重要な役割を持っています。{}の範囲を意識しなが

らソースを読んでいってください。ここからはメソッドごとに説明をしていきます。ソース全体

は(配布します)を参照してください。それではクラスの中を見ていきましょう。 変数の定義 いろいろなメソッドで利用できるようにメソッドの外にいくつかの変数を定義しましょう。 メソッドの中でなくクラス内に定義する変数のことを Java ではフィールドと呼んでいます。

フィールドは必要に応じて他のクラスからどのように見えるかという可視性を設定することが

できます(表 1.2)。private int row = 9;は private となっていますので BombGameActivity の中

のメソッドからしか参照できません。 private final int MENU01 = 0;のところの final は一度しか代入できないという意味で、finalを使うと定数として宣言を行うことができます。一般的に定数の名前は大文字で記述します。 この MENU01 と MENU02 は Android 端末のメニューボタンを押したときに表示されるメ

ニューの項目を識別するために作成した変数です。また Chronometer は Android SDK に用意

されているクラス部品の一つで画面上にタイマーを簡単に表示できるようになるものです。ここ

では経過時間の測定に利用します。

151

Page 44: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

初に実行される OnCreate Android のアプリケーションを起動したときに 初に実行されるメソッドが OnCreate です

リスト①。アプリケーション実行時に OnCreate の中が上から下に順番に実行されていきます。

OnCreate ではまず以下のことを実行しています。 1) 通知バーをなくす 2) タイトルバーをなくしアプリケーションを全画面表示する

通常 Android のアプリケーションは 上部に通知バーが表示されます。通知バーには電話の着

信や時刻、バッテリーの状態など外部の通知が表示されます。 この通知バーは OS 側の Window 領域になります。通知バーを非表示にするには

BombGameActivity クラスから getWindow()で WindowManager クラスを呼び出し、

FLAG_FULLSCREEN をセットします。 一方、アプリケーションのタイトルバーは BombGameActivity クラスの領域になるので、タ

イトルバーを非表示にするには Activity クラスの requestWindowFeature を呼び出し、

FEATURE_NO_TITLE をセットします。 super.onCreate(savedInstanceState);

は super クラスすなわち継承している Activity クラスの onCreate を実行しています。 init()…②と setBomb()…③、getTableLayout()…④は自分で作成するメソッドです。init()は

初期設定のためのメソッドで設定値の読み込みやタイマーのセットを実装します。setBomb()はbombNum で定義した数の爆弾をマス目にランダムにセットします。getTableLayout()はレイア

ウトを生成するメソッドです。Android には画面に何かを並べるときに様々なレイアウトで並べ

るためのレイアウトクラスが用意されています (表 4)。今回はマス目に並べたいので

TableLayout を利用します。 後に生成された TableLayout を setContentView()を利用し画面

にセットします。それでは個々のメソッドの中を見ていきましょう。 初期処理 リスト②の init()メソッドでは初期処理として以下のことを実装しています。 1) 設定ファイルを読み込みマス目と爆弾の数をセットする 2) 経過時間の測定するために開示時刻を記録する

Androidでは設定情報を読み書きするためにSharedPreferencesクラスが用意されています。

SharedPreferences クラスを利用すると設定情報を XML ファイルに保存し作成している

BombGameActivity クラスなどで読み込みや書き込みを行うことができます。保存は後述の設

定画面(SettingsActivity クラス)で行います。ここでは設定されている情報の読み込みを行って

います。 SharedPreferences 利用して設定情報を読み込むために getSharedPreferences を利用します。

第一引数の”pref”は保存されているファイル名で、第二引数の MODE_PRIVATE は設定情報を、

そのアプリケーション内のみで利用するという意味です(表 5)。 row = pref.getInt("H", 9);

設定情報は保存した型に合わせて getInt(キー名, デフォルト値)などを利用し取得します。

ここでは取得した情報を row などフィールドにセットし他のメソッドから利用できるようにし

ています。 次の

chronometer = new Chronometer(this);

では chronometer クラスを BomGameActivity クラスで使えるようにしています。この時点

で chronometer に生成時の時刻が保存されます。 爆弾をセットする

setBomb()…③メソッドを見ていきます。

152

Page 45: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

まず、マス目の行(row)と列(col)から全体のマス目の数を求め、爆弾の位置をセットするた

めの配列の数とします。次に爆弾の設置数分、繰り返し処理を行い、ランダムな位置を取得し、

まだ爆弾がセットされていない場合に爆弾をセットします。 画面のレイアウトをセットする

getTableLayout()…④ではマス目を Table レイアウトで生成しています。TableLayout は表状

に配置するためのレイアウトで TableRow とともに利用します。TableLayout が表全体を示す

のに対し TableRow は表の各行に相当します。TableRow にボタンなどの部品を配置することに

より、左から順にボタンなどの部品を配置することができます。 Button には ID のセットや高さや幅のセットを行います。ID は Button を識別するためのも

のです。クリックなどのイベントが起きた時にどのボタンが押されたか区別するために利用しま

す。ここではボタンの ID に爆弾をセットするときに利用した配列の位置と一致させる形で 0 か

ら始まるセルの位置をセットています。 セルの幅や高さは端末の画面の大きさに依存するので画面の大きさ取得し計算します。別途作

成する getCellHeight()メソッド…⑥や getCellWidth()メソッド…⑦を使います。 Button の setBackgroundDrawable はボタンに対して背景画像をセットするメソッドです。

画像のようにプログラム中で利用するプログラム以外のものをリソースといいます。Androidではリソースは res というフォルダーの中に保存します。背景画像はこの res フォルダーの中に

drawable というフォルダーを用意しそこに格納します。 注)通常は画面の解像度に合わせて複数の画像を用意します。そのためプロジェクト作

成時 drawable-hdip(高解像度)、drawable-mdpi(中解像度)、drawable-ldpi(低解像度)のフォルダーが用意されています。今回は一つの画像解像度で対応したいので

drawable フォルダーを作成しそこに画像を格納しています。 プログラムから格納した画像ファイルへアクセスするには、リソース ID という ID を利用し

てアクセスすることになります。リソース ID とは Android で用意されている特別な仕組みでリ

ソース(アイコン画像やプログラムで利用する画像、ビデオなど)に対してつけられる ID です。

リソース ID は gen フォルダーの中の R という Java ファイルに定義されます。たとえば

drawable フォルダーの中に offtile.png という画像を追加したとします。すると自動で R.javaファイルの drawable の中に public static final int offtile=0x7f020004;

が追加されます。この R.java に追加される ID を利用してプログラムからリソースにアクセ

スができるようになります。drawable に格納した offtile.png ファイルにアクセスするには、

R.drawable.offtile のように記述します。リソース ID が分かると getResources().getDrawable()メソッドにより画像を取得できるようになります。 イベントをセットしイベント時の内容を実装する ⑤の setEvent()メソッドはマス目にセットするボタンに対してイベント処理を実装している

部分です。今回一番長いメソッドになります。下記を実装しています。 ボタンの setOnClickListener()メソッドにてクリック時のリスナーを登録する 1) onClick()メソッドでクリックされた時の動作を実装する 2) クリックされた時の動作は

(ア) ボタンを押せないようにする (イ) ボタンの ID を取得し爆弾の位置であった場合、爆弾の画像をセットし、ゲームオーバ

ーダイアログを表示する (ウ) 爆弾の周囲にあるタイルを計算し隣接する爆弾の数を数える (エ) 隣接する爆弾の数に応じた背景画像をセットする (オ) 爆弾以外のタイルを開いたらおめでとうダイアログを表示する

153

Page 46: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

3) ボタンの setOnLongClickListener()メソッドにてロングクリック時のリスナーを登録する 4) ロングクリックされた時の動作

(ア) ボタンがまだ押されていない場合は背景画像を旗にセットする 画面の大きさからセルの幅と高さを算出する getCellHeight()…⑥メソッドと getCellWidth()…⑦メソッドはそれぞれ画面の大きさを取得

し各セルの幅や高さを算出しています。 画面の幅や高さを求めるには WindowManager クラスを利用します。WindowManager から

getDefaultDisplay を利用し画面を担当する Display クラスを取得します。 Display クラスの getHeight()や getWidth()を利用することにより、画面の幅や高さを求める

ことができます。後は行と列の数でそれぞれ割って各セルの幅と高さを求めています。 メニューの生成とメニュー選択時の動作 メニューを生成するには onCreateOptionsMenu()メソッドをオーバーライドします。メニュ

ーが選択された時の動作を実装するには onMenuItemSelected()メソッドをオーバーライドし

ます。ここでは「盤面の変更」と「リセット」をメニューからできるようにします。 メソッドをオーバーライドするには、Eclipse のソースメニューから「メソッドのオーバーラ

イド/実装」を選択します。onCreateOptionsMenu と onMenuItemSelected を選択し OK ボタ

ンを押します。 すると下記のように自動生成でメソッドが入りますので後はリストのように書き換えていき

ます。 @Override

public boolean onCreateOptionsMenu(Menu menu) {

// TODO 自動生成されたメソッド・スタブ

return super.onCreateOptionsMenu(menu);

}

@Override

public boolean onMenuItemSelected(int featureId, MenuItem item) {

// TODO 自動生成されたメソッド・スタブ

return super.onMenuItemSelected(featureId, item);

}

メニューを追加するには menu の add()メソッドを利用します。 menu.add(Menu.NONE, MENU01, Menu.NONE, "盤面の変更");

初めの引数はグループ ID というもので可視性などの情報をグループ化したい場合に利用し

ます。通常は Menu.NONE として 0 をセットします。2 番目の引数の MENU01 はフィールド

で定義した固有のメニューの ID を示しています。3 番目の引数は表示順で Menu.NONE とし

て 0をセットしています。Menu.NONE とした場合は追加した順番にメニューが表示されます。

4 番目の引数「盤面の変更」はメニューに表示される文字を示しています。 メニューが選択されると onMenuItemSelected()が呼ばれます。item.getItemId()にて押され

たメニューの ID(MENU01 か MENU02)を取得し switch 文を利用し処理を分岐しています。 「盤面の変更」が選択された場合、設定画面を開きます。設定画面は新しい画面になるので別

の Activity になります。別の Activity を起動するには Intent という機能を利用します。Intentとは Android で重要な機能の一つで、ある Activity とある Activity の橋渡しをする機能です。

Intent は「意図・目的」といった意味なので、Intent を利用するということは、ある Activityを起動するときの意図といった意味になります。

この Intent を利用することにより自分のアプリケーションの別の Activity を起動できるだけ

でなく、他のアプリケーションを起動することもでき、自在にアプリケーション間の連携を図る

154

Page 47: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

ことができます。 Intent を取得するには、自分自身(BombGameActivity)である this と起動したい、Activity

名.class を指定します。後は startActivity()メソッドを使うことにより別の Activity を起動する

ことができます(現時点では SettingsActivity クラスは作成していないのでエラーとなります)。 リセットが選択された場合は、次に記述する自身のリセットメソッドを実行します。 盤面をリセットする リスト⑩の reset()メソッドでは、初期処理の実行、爆弾のセット、レイアウトのセットを行

い、画面に再度レイアウトをセットしています。 ゲームオーバー時のダイアログとゲームクリアー時のダイアログの表示 ゲームオーバー時やクリアー時はそれぞれ gameOverDialog()…⑪、congratulationsDialog()…⑫のメソッドが onClick()内から呼び出され実行されます。

経過時間を計算するために SystemClock.elapsedRealtime()のシステムの時刻から、init()メソッドでクロノメーターを作成したときの時刻を chronometer.getBase()にて引き算します。msなので 1000 で割って秒にします。次に取得した秒を String クラスの format を利用し、00:00の形式に変更します。 ダイアログを出すためには Builder クラスを利用します。builder に対して setTitle()メソッド

でタイトルをセットし、setMessage()メソッドでメッセージをセットします。show()メソッドで

実際にダイアログを表示します。 後にタイルを開けたカウントを格納している openTile を 0 にセットします。 実際に表示されるダイアログは下記の通りです

このダイアログには閉じるボタンを付けていませんので、ダイアログを閉じるには戻るボタン

を押します。 設定画面を作ろう 設定画面は別の画面になるので新しいクラスを作成します。Android のアプリケーションの画

面は Activity クラスを継承したものになりますので、BombGameActivity と同様に新しく作る

クラスも Activity を継承したものになります。SettingActivity というクラスで作成します。 クラスを新規に作成するにはパッケージ・エクスプローラーから src を展開し、パッケージ名

を右クリックしドロップダウンリストの新規からクラスを選択します。 新規クラスのダイアログが表示されますので、SettingsActivity と入力します。次に「スーパ

ークラス」の「参照…」を押します。ダイアログが表示されますので「型を選択してください」

の下のテキストボックスに「android.app.」と入力します。すると「Activity-andrid.app」が

一番上に出てくるので、Activity を選択し OK ボタンを押します。 後に完了ボタンを押してダ

イアログを閉じます。 設定画面は設定を行うための簡易な画面なので、onCreate()メソッドの中でレイアウトの作成、

ボタンのイベント設定、設定ファイルの読み書きのすべてを行います。 まず、フィールドに幅や高さを示す W, H や画面に表示する部品(クラス)を定義します。

onCreate()メソッドの中でこれらの変数を定義することも可能ですが、フィールド内に定義する

ことにより、イベントが発生したときに呼び出される onClick()メソッド内からこれらのフィー

ルドにアクセスができるようになります。 onCreate()メソッドでは下記のことをやっています。 1) 設定ファイルを読み込み初期表示用に値をセットする 2) 画面に表示する部品をプログラム内で使えるようにする 3) 画面に表示する文字列をセットする 4) 画面に部品を張り付ける 5) OK ボタンが押された場合

155

Page 48: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

(ア) 入力されている設定値を確認して保存する 6) キャンセルボタンが押された場合

(ア) 画面を終了し元の画面に戻る ここではいくつか新しい Android SDK に定義されている部品(クラス)が出てきますので、こ

れらを中心に解説したいと思います。 画面の部品配置はレイアウトにより決まります。BombGameActivity では TableLayout を利

用しました。ここではボタンなどを縦や横にまっすぐ並べる LinearLayout を利用します。 linearLayout.setOrientation(LinearLayout.VERTICAL);//縦に並べる

とすることで、縦に並べる設定になります。 テキストラベルの表示には TextView を利用します。 textViewH.setText("高さ:");

などとし、ラベルの文字列をセットすることができます。 入力フィールドの表示には EditText を利用します。 editTextH.setText(String.valueOf(H));

などとし、初期値をセットしています。H は整数の int 型なので String.valueOf()メソッドを使

って文字列に変換しています。 設定した部品は linearLayout.addView(textViewH);

などとすることにより、レイアウトに張り付けることができます。 OK のボタンである buttonOK とキャンセルのボタンである buttonCancel の onClick()メソ

ッド内にそれぞれイベントが起きた時の動作を記述します。 OK ボタンが押された場合は、

editTextH.getText().toString() にて入力された値を取得しています。入力された値は文字列なので、この文字列を

Integer.parseInt を利用し整数に変換しています。 場合によっては数字でない文字が入力され整数に変換できないかもしれません。そのようなと

きは実行時のエラーである例外が発生してしまいます。この例外を処理するために、try-catchという構文を利用します。 try{}のブロックの中で整数に変換できない例外が発生すると catch(){}のブロックに処理が移

ります。この catch の中で Android SDK に用意されているメッセージを表示する Toast という

機能を利用し、「半角数字を入力してください」とのメッセージを表示しています。 キャンセルボタンが押された場合は、finish()を利用し SettingsActivity を終了します。 AndroidManifest.xml に宣言しよう 新規に作成した Activity はそのままだと呼び出すことができません。Android には

AndroidManifest.xml というファイルが用意されていて、そのアプリケーションの機能概要を

記述するルールになっています。 たとえばインターネットに接続するアプリケーションを作成するには

<uses-permission android:name="android.permission.INTERNET"/>

という記述が必要になりますし、バイブレーションを使う場合は <uses-permission android:name="android.permission.VIBRATE"/>

の記述が必要になります。これにより例えば Android マーケットからアプリケーションをダウ

ンロードするときに、このアプリケーションがどのようなものなのか表示され、利用者がそれを

確認することができるようになります。 新規に Activity を追加した場合もこの AndroidManifest.xml に記述する必要があります。パ

ッケージ・エクスプローラーを展開した中にある AndroidManifest.xml をダブルクリックで開

156

Page 49: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

きます。すると下記のような画面になります。このまま GUI 上で編集してもいいのですが、こ

こでは XML ファイルを直接編集したいと思います。下部のタブにある AndroidManifest.xmlをダブルクリックします。すると下記のような画面になります。 <application android:icon="@drawable/icon" android:label="@string/app_name">

</application>

はアプリケーションの設定を行っているところで、アイコンの設定やアプリケーション名の設定

を行っています。この<application> </application>の中に使用するActivityを定義します。プロ

ジェクトを作成したときに、BombGameActigityについては自動で定義されています。 <activity android:name=".BombGameActivity"

android:label="@string/app_name"

>

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

android.intent.action.MAINの部分はこのActivityが 初に実行されることを示しています。

android.intent.category.LAUNCHERの部分はAndroid端末のアプリケーションラウンチャー

に表示するとしています。 SettingsActivityを下記のように定義します。 <activity android:name=".SettingsActivity"

android:label="@string/app_name">

</activity>

これで SettingsActivity が登録され、きちんと動作するようになります。AndroidManifest.xmlに登録漏れがあると実行時の例外になるので、気を付けましょう。実行時の例外を確認するには

下記の Log の出し方と書き方を参考にしてください。 LogCat の出し方と書き方 Android ではエミュレーションなどその端末内の動作をログで確認することができます。

Android のログは LogCat と呼ばれます。LogCat を表示するには Eclipse の Window メニュー

から「ビューの表示」→「その他」を選択します。するとダイアログが表示されますので「Android」を展開し「LogCat」を選択し OK ボタンを押します。Eclipse 下部の情報表示エリアに LogCatが表示されます。 ログを記述する際はソース内に

Log.d("BombActivity",”some log message”); のように記述します。Log を初めて使う場合は import 文が必要になるので、Log をポイントし

Ctl+1 を押すなどしてインポートしてください。"BombActivity"の部分はタグと呼ばれるもので

自由に設定します。”some log message”のところは出力したいメッセージを記述するところです。

残念ながら本稿執筆時点では日本語の出力は文字化けしてしまうようです。 スクリーンキャプチャの方法 画面のスクリーンキャプチャを取るには、C:¥pleiades¥android-sdk-windows¥tools 内の

ddms.bat というアプリケーションを利用します。起動すると下記のようなアプリケーションが

起動します。 エミュレーターが起動していると左上にエミュレーターが表示されますのでエミュレーター

を選択します。Device メニューから Screen catpure を選択するとスクリーンキャプチャを取る

ことができます。

157

Page 50: Google Android アプリケーション4. Android のデバイス制御やイベントについて理解できる 第1 部 View とSurfaceView クラスを使ったプログラム

実機で確認できる人は 実機が手元にある人は実機を USB ケーブルで PC と繋いでみましょう。初めて繋いだときは

ドライバのインストール画面になります。「いいえ、今回は接続しません」にチェックを入れ「次

へ」ボタンを押します。すると下記ダイアログになるので、「一覧または特定の場所からインス

トールする(詳細)」にチェックを入れ「次へ」ボタンを押します。次のダイアログで「次の場所

で 新のドライバを検索する」と「次の場所を含める」にチェックを入れ、「参照ボタン」を押

します。C:¥pleiades¥android-sdk-windows¥usb_driver を選択し「OK ボタン」を押します。

「次へ」ボタンを押すとドライバのインストールが始まります。 ただし、Android の新しい機種は Android SDK 内のドライバが対応していない場合がありま

す。そのような場合はドライバの設定ファイルを書き換えるなどの処理が必要です。Android SDK に用意されていないドライバの場合は端末の SD カードに格納されている場合が多いよう

です。インターネットなどで「該当の機種名 USB ドライバ」で検索してみてください。 ドライバが認識すれば実機での確認はエミュレーターと同じ方法で行うことができます。エミ

ュレーターが起動した状態で、実機を USB ケーブルで繋ぎ実行を行った場合は下記のようなエ

ミュレーターか実機かを選択する画面になりますので、実機を選択し OK ボタンを押してくだ

さい。 実機を使うとセンサーやバイブレーションなどエミュレーターでは確認できなかった動作を

確認することができるようになります。手元に Android の端末がある人はぜひ実機で確認して

みてください。 BombGameActivity のソースコードでコメントアウトされている下記のコードのコメントを

外し、AndroidManifest.xml に uses-permission の宣言を入れることにより、タイルを開けた

時と爆弾を開けた時に振動するようになります。 BombGameActivity のコメントアウト部分は

//Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);

//vibrator.vibrate(300);//ms

//vibrator.vibrate(50);//ms

です。先頭の//を外し命令を有効化します。 AndroidManifest.xml の</application>の下に追加するパーミッションの設定は下記のとおり

です。 <uses-permission android:name="android.permission.VIBRATE"/>

158