2 章 Android グラフィックスによる Java 入門
Androidアプリを作るためにはJavaとXMLの知識が必要になります。JavaやXMLを
本格的に学ぶにはそれぞれ入門書が必要になります。本書ではAndroidアプリを作りなが らJavaもXMLも手っ取り早く学べるように工夫してあります。そこでまず、Androidグ ラフィックスを利用して画面に文字、直線、イメージなどを描画するプログラムを例にJava の基本的な言語仕様について学びます。Androidグラフィックスを例にしたのは、Android グラフィックスではXMLの知識は必要ないこと、視覚的にも興味のある結果が得られ学習 のモチベーションが上がることです。
この章ではAndroidアプリを作る上で当面必要なJavaの基礎知識として以下の内容を説 明します
・オブジェクト指向言語特有の概念
クラス、インスタンス(オブジェクト)、コンストラクタ、メソッド
・C言語などの基本言語と共通の概念
変数、演算子、for文やif文などの流れ制御文、配列
・Androidグラフィックスに特有な概念 描画メソッド、ビットマップ
「注」この章のプログラムリストについて
最初の例題のみ全リストを掲載してありますが、その後の例題はGViewクラス内のみが 変更されるので、GViewクラスだけをリストとして掲載してあります。最初の例題を
Graph1としています。その後の例題はGraph2、Graph3・・などと別なプロジェクトを
新たに作ってもよいですが、Graph1のonDraw内だけを変更してもよいです。ただしこの 場合は前に作ったプログラムは無くなってしまいますので注意してください。
2-1 Android グラフィックスの基礎
最初の例は「Android」という文字をグラフィックスで表示します。Androidグラフィッ クスはViewクラスを元にして行います。描画処理はonDrawメソッドの中に記述します。
描画に当たっては、まずPaintクラスのメソッドを使って描画色やテキストサイズを指定
し、次にCanvasクラスのメソッドを使ってテキストの描画を行います。
1. View クラス
グラフィックスを描画する画面はViewクラスを継承して作ります。以下はGViewとい う名前のユーザ定義クラスを作っています。コンストラクタのGViewはスーパークラスの コンストラクタを呼び出す部分でこれが定型です。ユーザが行う描画処理はonDrawメソ ッド内に記述します。onDrawメソッドが呼び出されたときにCanvasクラスの引数canvas に描画オブジェクトが渡されますので、このcanvasに対し描画メソッドを使ってグラフィ ックス処理を行います。onDrawメソッドの仮引数を「Canvas canvas」としていますが、
仮引数の名前はなんであってもよいので「Canvas c」などとしても構いません。
private class GView extends View { ←Viewクラスを継承したユーザ定義クラスGview public GView(Context context) { ←コンストラクタ
super(context);
}
protected void onDraw(Canvas canvas) { ←onDrawメソッド 描画内容を記述 ↑Canvasクラスの引数canvas }
}
このGViewクラスを実際に画面に設定するには、「setContentView(R.layout.main);」
の代わりに「setContentView(new GView(this));」とします。前者はウイジェットを配置 したレイアウトを表示し、後者はグラフィックス画面を表示します。
2. Paint クラスとオブジェクト(インスタンス)
Paintクラスは描画色や文字サイズなどの描画情報を扱うクラスです。クラスからインス
タンスを生成するにはnew演算子を用いて、以下のように宣言します。コンストラクタは クラス名と同じ名前の特別なメソッドで初期化を行うものです。これでpaintという名前の
Paintクラスのオブジェクトが生成されます。以後paintに対しメソッドを適用します。
↓クラス名 ↓コンストラクタ Paint paint=new Paint();
↑オブジェクト(インスタンス)
「注」オブジェクトとインスタンス
クラスはオブジェクトを生成するための金型(テンプレート)のようなものと考えるこ とができます。オブジェクト指向言語では、金型から実際に生成された実体をオブジェク トと言い、クラスからオブジェクトを作ることをインスタンス化(instantiation)と言い ます。C++ではクラスを実体化したものをオブジェクトと呼び、Javaではインスタンス化 したオブジェクトをインスタンスと呼びます。この場合インスタンス=オブジェクトと考 えてよいです。本書では「インスタンス」という言葉が適切な場合以外は「インスタンス」
と「オブジェクト」を使い分けずに「オブジェクト」と呼ぶことにします。
3. Paint クラスのメソッド
描画色を緑、フォントのサイズを40ピクセルに設定するには、paintオブジェクトに対 しsetColorメソッド、setTextSizeメソッドを使って以下のようにします。「Color.BLUE」
は青色を示すColorクラスの定数です。
paint.setColor(Color.BLUE); ←青色
paint.setTextSize(40); ←テキストのサイズを40ピクセル
4. Canvas クラスの描画メソッド
onDrawメソッドの引数canvasに対し描画を行うにはdrawTextメソッドを用います。
指定するx,y座標はテキストの左下隅の座標です。
canvas.drawText("Android", 10,50, paint);
↑描画位置のx,y座標
Android
↑左下隅の座標を表示位置として指定
「注」メソッドとは
メソッドはある処理を行う機能単位で、ユーザは引数というデータをこのメソッドに渡 して目的の処理を行います。
「注」メソッドの種類
onDrawとdrawTextはどちらもメソッドですが、呼び出し方(呼び出され方)が異なり
ます。通常onXXXメソッドはシステムから呼び出されるメソッドで、処理内容をユーザが 定義します。たとえば、onDrawメソッドは画面に描画を行う必要が生じたときに呼び出さ れます。これに対し「オブジェクト.メソッド(引数,・・・)」の形式で呼び出すメソッドは ユーザが直接メソッドを呼び出します。
「注」インデント
プログラムのブロック構造を分かりやすくするため内側のブロックになるほど書き出す 位置を右にずらすことをインデント(字下げ)と言います。Eclipseでは自動的に4文字幅 のインデントをとります。
「例題2-1」以上をまとめた全リストを示します。それぞれの位置関係を確認してください。
・MainActivity.java
package com.example.graph1;
import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Bundle;
import android.view.View;
import android.view.Menu;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(new GView(this));
}
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setTextSize(40);
canvas.drawText("Android",10,50,paint);
} }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
} }
「注」onCreateOptionsMenuメソッドは削除しても構いません。
「注」背景色
古い開発環境(2012年6月頃までのAndroid SDK)ではデフォルトの背景色は黒背景で したが、新しい開発環境では白背景です。デフォルトの背景色を変えるには onDraw メソ ッドの先頭で以下のように背景色を設定します。背景白にするにはColor.WHITEとします。
Protected void onDraw(Cavas canvas){
canvas.drawColor(Color.BLACK);
「注」Android Lint Checks
古い開発環境では以下のようにonDrawメソッド内でPaintオブジェクトを取得しても 警告エラーになりませんでした。
protected void onDraw(Canvas canvas) { Paint paint=new Paint();
paint.setColor(Color.BLUE);
paint.setTextSize(40);
canvas.drawText("Android",10,50,paint);
}
新しい開発環境では「Paint paint=new Paint();」のところで「Avoid object allocations during draw/layout operations (preallocate and reuse instead)」という警告エラーがでま す。これはonDrawが呼ばれるたびに Paintオブジェクトを生成するという無駄を止め、
GView 内で一度生成したものを使いまわしなさいということです。このような厳しいチェ
ックを「Android Lint Checks」と言います。
このため、本書ではpaintをメソッド外で宣言し、GViewコンストラクタ内でPaintオ ブジェクトを生成し、onDrawメソッドでpaintを使用するという多少煩雑な方法をとって います。
private class GView extends View { private Paint paint; ←paintの宣言 public GView(Context context) { super(context);
paint=new Paint(); ←Paintオブジェクトの生成 }
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE); ←paintの使用 paint.setTextSize(40);
canvas.drawText("Android",10,50,paint);
} }
警告エラーを無視するなら上のようにonDraw内に全てまとめる方が簡単です。
2-2 for 文による繰り返し
Javaに限らずどのようなプログラム言語においてもプログラムの流れを制御する制御文 があります。JavaはC系の言語と同じ流れ制御文を使います。たとえば繰り返しを行うfor
文やwhile文、条件判定を行うif文、else if文、switch文などです。この章ではこれらの
制御文の使い方を説明します。switch文については3-9で説明します。
1. for 文
for文は決められた回数の繰り返しに使います。たとえば、次の例は、変数iを1から始 め、10以下の間、iを+1しながら、{と}で囲まれた範囲(ブロック)を繰り返します。{ } 内に書く文が1つのときは{ }を省略できます。繰り返しのことをループと呼びます。for 文の繰り返しを制御している変数のことをループ変数と呼びます。
↓初期値 for (i=1;i<=10;i++){
・ ↑ ↑増減式
・ 終了条件式 }
「例」
for (i=5;i<=10;i++)
・・・iを5から始めiを+1しながら10まで繰り返す。6回繰り返すことになります。
for (i=10;i>0;i--)
・・・iを10から始め、iを-1しながら0より大きい間繰り返す。10回繰り返すことにな ります。
for (x=-2.0;x<=2.0;x+=0.5)
・・・xを-2.0から始め,+0.5しながら2.0になるまで繰り返します。
「例題2-2」Androidという文字を5個縦に描きます。表示するテキストのx座標を10と
し、y座標を40、80、120、160、200と変化します。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setTextSize(40);
for (int y=40;y<=200;y+=40){
canvas.drawText("Android",10,y,paint);
} } }
「練習問題2-2」例題2-2と同じことをループ変数iを1~5まで変化させて行いなさい。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setTextSize(40);
for ( ① ){
canvas.drawText("Android",10, ② ,paint);
} } }
2-3 変数の役割
変数はプログラムの進行によってその値が変わります。この意味から「変わる数=変数」
と呼びます。これに対し10や"hello"などの変化しないものを定数と呼びます。
1. 変数の宣言
変数は使用する前に、「int i;」のように宣言します。これでint型(integer:整数型)の 変数iが宣言されました。複数の変数を一緒に宣言するには各変数をコンマ(,)で区切り、
「int i,j;」のように宣言します。
for文で使うループ変数はfor文の外で
int i;
for (i=0;i<=10;i++)
のように宣言してもよいですが、次のようにfor文の中で宣言することもできます。
for (int i=0;i<=10;i++)
2. 変数名の付け方
変数に付ける名前を変数名と呼びます。変数名には名前付け規則がありますが、おおよ その規則は以下です。
・英字で始まる英数字。a1やsumなど。
・英大文字と英小文字を区別する。SUM,Sum,sumはいずれも異なる変数名となる。
・予約語を使用してはいけないが、それを含むことはできる。たとえば、forは認められな
いがforceは認められる。
変数名はユーザが決めれば良いわけですが、ある程度の共通ルールに従った方がプログラ ムを読みやすくします。
・forなどのループ変数にはi,j,kなどを使う
・変数の役割を連想できる変数名を使う。合計ならsumなど。
3. 演算子
データを加減乗除するものを演算子と呼びます。i++は変数iの内容を+1します。i--は変 数iの内容を-1します。++を増分演算子(インクリメント演算子)、--を減分演算子(デク リメント演算子)と呼びます。i++はi=i+1と同じ意味、i--はi=i-1と同じ意味です。i+1の
+は加算演算子、i-1の-は減算演算子です。
x+=0.5はxの内容を+0.5します。x=x+0.5と同じ意味です。+=を複合代入演算子と呼び ます。
4. 文字列の連結
文字列は「"」で囲って、"Android"のように表します。文字列は「+」演算子を使って連 結することができます。"123"+"45"は"12345"となります。「文字列+数値」または「数値
+文字列」の場合は数値が文字列に変換されて連結されます。
「 例題 2-3」 ループ 変数の i を使 って「"Android"+i」 とするこ とで、"Android1"、
"Android2"・・・、"Android5"という文字列を作り出します。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setTextSize(40);
for (int i=1;i<=5;i++){
canvas.drawText("Android"+i,10,i*40,paint);
} } }
「練習問題2-3」1+2+・・・+99+100の合計値を変数sumに求め表示しなさい。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setTextSize(40);
int sum=0;
for (int i=1;i<=100;i++){
① }
canvas.drawText("合計="+ ② ,10,40,paint);
} }
2-4 イメージの表示
AndroidではPNGやJPG(JPEG)などのイメージをView画面に表示することができ
ます。デフォルトでres/drawableにic_launcher.pngというイメージファイルが用意され ているので、これを表示する方法を説明示します。
1. リソースからビットマップを作製する
res/drawableにあるic_launcher.pngをリソースとして管理するときのリソースID(識
別番号)は「R.drawable.ic_launcher」です。イメージリソースからグラフィックス描画を 行うためのビットマップを作るにはBitmapFactoryクラスのdecodeResourceメソッドを 使います。decodeResourceメソッドの第1引数にはアプリケーションのリソースを管理し ているリソース管理オブジェクトを指定します。このオブジェクトは
「getContext().getResources()」または「getResources()」で取得できます。第2引数には イメージに割り当てられたリソースIDを指定します。
↓リソース管理オブジェクト
Bitmap img = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
↑リソースID
2. ビットマップの描画
ビットマップを描画するにはCanvasクラスのdrawBitmapメソッドを使います。最後
の引数はPaintクラスのオブジェクトを指定して描画モードを設定しますが、ビットマッ
プをそのままのイメージで描画する場合は「null」を指定します。表示位置の(x,y)はイメー ジの左上隅の座標となります。
↓ビットマップ canvas.drawBitmap(img,x,y,null);
↑表示位置(x,y)
「例題2-4」R.drawable.ic_launcherで示すイメージを(0,0)位置に描画します。
private class GView extends View { private Bitmap img;
public GView(Context context) { super(context);
img=BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
}
protected void onDraw(Canvas canvas) { canvas.drawBitmap(img,0,0,null);
} }
「注」Android Lint Checks
Paintオブジェクトの場合と同様に、onDraw内でBitmapリソースを取得すると警告エ
ラーとなります。
protected void onDraw(Canvas canvas) {
Bitmap img = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
canvas.drawBitmap(img,0,0,null);
}
「注」ic_launcher.pngのサイズ
res/drawable-hdpiに格納されているic_launcher.pngのサイズは72×72ピクセルです。
「練習問題2-4」R.drawable.ic_launcherで示すイメージを4か所に描画しなさい。
private class GView extends View { private Bitmap img;
public GView(Context context) { super(context);
img=BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
}
protected void onDraw(Canvas canvas) { canvas.drawBitmap(img,0,0,null);
canvas.drawBitmap(img, ① ,null);
canvas.drawBitmap(img, ② ,null);
canvas.drawBitmap(img,72,72,null);
} }
2-5 イメージリソースの配置
ic_launcher.pngはデフォルトで配置されていますが、ここではwhite.png(60×60ピク
セル)というイメージファイルを「res/drawable-hdpi」フォルダに置きます。
white.png
ビットマップ・イメージimgの幅wと高さhを取得するには次のようにします。
int w=img.getWidth();
int h=img.getHeight();
「例題2-5」white.pngを横に5つ並べて描画します。イメージの横幅wを取得し、その値
を元に表示するx座標をi*wで求めます。
private class GView extends View { private Bitmap img;
public GView(Context context) { super(context);
img=BitmapFactory.decodeResource(getResources(), R.drawable.white);
}
protected void onDraw(Canvas canvas) { int w=img.getWidth();
for (int i=0;i<5;i++){
canvas.drawBitmap(img,i*w,30,null);
} } }
「練習問題2-5」white.pngイメージを横に5列、縦に2行で描画しなさい。
private class GView extends View { private Bitmap img;
public GView(Context context) { super(context);
img=BitmapFactory.decodeResource(getResources(), R.drawable.white);
}
protected void onDraw(Canvas canvas) { int w=img.getWidth();
int h= ① for (int i=0;i<5;i++){
canvas.drawBitmap(img,i*w,30,null);
canvas.drawBitmap(img,i*w, ② ,null);
} } }
1. 扱えるイメージフォーマット
Androidで扱えるイメージフォーマットはBMP(Bitmap)、JPG(JPEG)、PNG、GIFで
す。この中でPNGが推奨とされています。写真画像などはJPGを使うことがよくありま すので、通常はPNGとJPGを使うことが多いでしょう。
2. drawable リソースの種類
drawableはイメージのリソースですが、異なる解像度やディスプレイサイズに対応する
ためにxhdpi(eXtra High)、hdpi(High)、mdpi(Middle)、ldpi(Low)の4種類のdpi
(dot per inch)リソースが用意されていて、実行している端末に合ったdpiのフォルダか ら自動的に画像を取得します。
このためdrawableリソースを示すIDは「R.drawable-hdpi.white」のようにdpiを特定 して指定せず「R.drawable.white」のようにします。ちなみにデフォルトで配置されてい るic_launcher.pngのサイズは96×96、72×72、48×48、36×36です。画面がWVGAの
場合はdrawable-hdpiフォルダのイメージが使われます。
3. null
nullはオブジェクトがどこも参照していないということを示す特別な定数です。たとえ ば「Bitmap img;」のようにBitmapクラスのimgを宣言した場合、imgにはオブジェクト が生成されていないので、この時点でのimgは「null」という値を保持しています。この 場合は「if (img==null)」のような使い方をします。
nullの別な使い方はメソッドの引数に指定する場合です。この場合はnullを指定した引 数はデフォルトの処理をすることになります。たとえば、drawBitmap(img, 0,0,null);の最 後の引数にnullを指定しています。最後の引数にはPaintクラスのオブジェクトを指定し て描画モードを指定することになっているのですが、デフォルトの処理でよいときにはnull を指定します。
4. インスタンス・メソッドと static メソッド
「canvas.drawBitmap」と「BitmapFactory.decodeResource」という2つのメソッドは 種類が異なります。前者をインスタンス・メソッド(通常これを単にメソッドと呼ぶ)、
後者をstaticメソッドと呼びます。「Canvas canvas」のCanvasはクラス、canvasはCanvas クラスのインスタンスです。drawBitmapメソッドはcanvasというインスタンスに対して その操作を行います。これに対しBitmapFactoryはクラス名ですが、この場合の
BitmapFactoryはBitmapFactoryクラスの唯一のインスタンスと考えることができ、この
唯一のインスタンスに対し、decodeResourceメソッドはその操作を行います。インスタン ス・メソッドはインスタンスごとに生成されるので、そこで扱うデータはインスタンスご とに固有のものです。これに対しstaticメソッドはいちいちインスタンスを生成せずに、
デフォルトで生成されいる静的なインスタンス(クラス名のインスタンス)に対して行う ため、データは共有されます。staticメソッドの代表的なものに、Mathクラスのsin、cos メソッドがあります。
「注」ここでは「インスタンス・メソッド」というJavaの用語の関係でインスタンスとい う言葉を使用していますが、オブジェクトと同じ意味です。
2-6 if else 文による条件判定
条件を判定し、その判定に応じて実行する処理を変えるにはif else文を使います。
1. if else 文
if else文は以下のように書きます。条件式を満たせば「文1」を実行し、満たさなければ
「文2」を実行します。else節に書くものがなければelse節全体を省略します。{ }内に書
く文が1つの時は{ }を省略することができます。
if (条件式) {
文1 ←条件を満たしたときに実行される文 }
else {
文2 ←条件を満たさないときに実行される文 }
条件式としては、次のようなものを書きます。条件式を満たしたときを真、満たさなか ったときを偽と呼びます。
a>1 変数aが1より大きいとき真
a==1 変数aが1のとき真
a==1 && b==1 変数aが1でかつ変数bが1のとき真 a==1 || b==1 変数aが1または変数bが1のとき真
2. 比較演算子
条件式において、大きい、小さい、等しいなどの大小比較を行う演算子を比較演算子と 呼び次の6つがあります。等しいは==と=を2つ書くことに注意してください。
比較演算子 意味
> 左辺の値が右辺の値より大きい。
>= 左辺の値が右辺の値より大きいか等しい。
< 左辺の値が右辺の値より小さい。
<= 左辺の値が右辺の値より小さいか等しい。
== 左辺の値と右辺の値が等しい。
!= 左辺の値と右辺の値が等しくない。
3. 論理演算子
1つの条件式の真と偽を否定(反転)する!演算子、2つの条件式を組み合わせて真・偽を 判定する&&演算子、||演算子を論理演算子と呼びます。
論理演算子 意味
! 否定(NOT)。真なら偽、偽なら真。
&& かつ(AND)。2つの条件式の両方が真のとき真。
|| または(OR)。2つの条件式のどちらかが真のとき真。
「例題2-6」白石と黒石を交互に4組み描画します。「i%2==0」が条件を満たすのはiが
0,2,4,6のときです。
private class GView extends View { private Bitmap white,black;
public GView(Context context) { super(context);
white=BitmapFactory.decodeResource(getResources(), R.drawable.white);
black=BitmapFactory.decodeResource(getResources(), R.drawable.black);
}
protected void onDraw(Canvas canvas) { int w=white.getWidth();
for (int i=0;i<8;i++){
if (i%2==0)
canvas.drawBitmap(white,i*w,30,null);
else
canvas.drawBitmap(black,i*w,30,null);
} } }
「注」 剰余演算子
%は整数値の除算の余りを求める演算子です。「10%3」は10を3で割ったときの余り
の「1」になります。剰余を使えば変数iの値が偶数か奇数か判定できます。「i%2==0」な らiは偶数と判定できます。
「練習問題2-6」1段目は白石、2段目は黒石から初めて例題2-6と同様に描画しなさい。
private class GView extends View { private Bitmap white,black;
public GView(Context context) { super(context);
white=BitmapFactory.decodeResource(getResources(), R.drawable.white);
black=BitmapFactory.decodeResource(getResources(), R.drawable.black);
}
protected void onDraw(Canvas canvas) { int w=white.getWidth();
int h=white.getWidth();
for (int i=0;i<8;i++){
if (i%2==0){
canvas.drawBitmap(white,i*w,30,null);
canvas.drawBitmap(black,i*w,30+h,null);
} else {
canvas.drawBitmap( ① ,null);
canvas.drawBitmap( ② ,null);
}
} } }
2-7 2 重ループ
ループの中にループが何重にも入る構造を多重ループと呼びます。for文の2重ループは 以下のような構造です。
for (int i=1;i<=2;i++){
for (int j=1;j<=3;j++){
A } }
ループ変数iが外ループ、ループ変数jが内ループを管理します。この2重ループは次の ように動作します。
外側のループを開始し、iが1のまま内側のループのjを1~3まで繰り返します。内側 ループの繰り返しが終了すると外側のループ変数iが2となり、再び内側のループを繰り返 します。
上の二重ループにおいて、A部におけるループ変数iとjの値をトレースすると次のよう になります。
i j
1 1
2 3
2 1
2 3
「例題2-7」リバーシーの盤面を示す8×8のマスをgreen.pngで描きます。ループ変数の
yが縦方向(y方向)、ループ変数のxが横方向(x方向)を管理します。(y,x)位置の表 示座標は(y*h,x*w)となります。実際にはタイトルバーから10ピクセル離すようにy*h+10 で表示します。xが内側のループになっているので、横方向にイメージが表示されていきま す。
private class GView extends View { private Bitmap green;
public GView(Context context) {
super(context);
green=BitmapFactory.decodeResource(getResources(), R.drawable.green);
}
protected void onDraw(Canvas canvas) { int w=green.getWidth();
int h=green.getHeight();
for (int y=0;y<8;y++){
for (int x=0;x<8;x++){
canvas.drawBitmap(green,x*w,y*h+30,null);
} } } }
「注」座標と行列
一般の数学の座標では(x,y)位置のようにxを先に記述しますが、プログラムではa[i][j]
のような配列の添字の使い方をして、行を示すiを先に記述します。この配列の行列を座標 に対応させると行がy、列がxとなるため、(行,列)という指定でいくと、(i,j)位置や(y,x) 位置のような表現をすることがあります。
「練習問題2-7」8×8のマスの対角線位置だけにgreen.pngで描画しなさい。対角線の条 件は「x==y 」または「 x+y==7」です。
private class GView extends View { private Bitmap green;
public GView(Context context) { super(context);
green=BitmapFactory.decodeResource(getResources(), R.drawable.green);
}
protected void onDraw(Canvas canvas) { int w=green.getWidth();
int h=green.getHeight();
for (int y=0;y<8;y++){
for (int x=0;x<8;x++){
if ( ① )
canvas.drawBitmap(green,x*w,y*h+30,null);
} } } }
2-8 else if 文
if else文は2方向の選択を行いますが、else if文は多方向選択を行います。else if文はif else文のelse節にifがネストしたものと考えられます。条件式1~条件式mまでが上から 順次評価され、一致したところの文を実行しelse if文から抜けます。一致する条件がなか ったときはelse節の文が実行されます。else節に書くものがなければ省略できます。文が 単文の場合は{ }を省略することもできます。
if (条件式1) { 文1 }
else if (条件式2) { 文2
}
・
・
else if (条件式m) { 文m
} else { 文n }
「例題2-8」白石と黒石を中央にクロスして置きます。白石は(3,3)と(4,4)位置、黒石は(3,4) と(4,3)位置になります。この条件で白石または黒石を判定し、いずれでもなければ緑盤面 とします。iが縦(y座標),jが横(x座標)を表します。
private class GView extends View { private Bitmap white,black,green;
public GView(Context context) { super(context);
white=BitmapFactory.decodeResource(getResources(),R.drawable.white);
black=BitmapFactory.decodeResource(getResources(),R.drawable.black);
green=BitmapFactory.decodeResource(getResources(),
R.drawable.green);
}
protected void onDraw(Canvas canvas) { int w=green.getWidth();
int h=green.getHeight();
for (int i=0;i<8;i++){
for (int j=0;j<8;j++){
if (i==3 && j==3 || i==4 && j==4)
canvas.drawBitmap(white,j*w,i*h+30,null);
else if(i==3 && j==4 || i==4 && j==3)
canvas.drawBitmap(black,j*w,i*h+30,null);
else
canvas.drawBitmap(green,j*w,i*h+30,null);
} } } }
「練習問題2-8」Nの文字を表すように石を描画しなさい。その際左端と右端は白石、対角 線は黒石としなさい。
private class GView extends View { private Bitmap white,black,green;
public GView(Context context) { super(context);
white=BitmapFactory.decodeResource(getResources(),R.drawable.white);
black=BitmapFactory.decodeResource(getResources(),R.drawable.black);
green=BitmapFactory.decodeResource(getResources(), R.drawable.green);
}
protected void onDraw(Canvas canvas) { int w=green.getWidth();
int h=green.getHeight();
for (int i=0;i<8;i++){
for (int j=0;j<8;j++){
if ( ① )
canvas.drawBitmap(white,j*w,i*h+30,null);
else if( ② )
canvas.drawBitmap(black,j*w,i*h+30,null);
else
canvas.drawBitmap(green,j*w,i*h+30,null);
} } } }
2-9 配列
たくさんのデータを効率よく管理するための便利なしくみ(データ構造)に、配列があ ります。配列は配列名と添字(そえじ)を用いて配列の各要素を参照します。配列は扱う 添字の個数により1次元配列、2次元配列などがあります。
1. 配列の宣言と参照
1次元配列は配列名と1つの添字で構成します。
↓配列名 ↓確保する要素の個数 int[] a=new int[10];
↑ ↑型名またはクラス名
これによりa[0],a[1],・・・a[9]という10個の要素からなる1次元配列が用意されます。配 列の基底要素は0から始まりますので0から9までの10個の要素が用意されます。a[10]
という要素は用意されていないことに注意してください。i番目の要素はa[i]で参照できま す。従って、配列の全ての要素を0にするには次のようにします。「a.length」で配列a[ ] の要素数(この例では10)を取得します。
for (int i=0;i<a.length;i++) a[i]=0;
2. 配列の初期化
配列の各要素に初期値を与えるには、配列の宣言時に{ }内に初期化するデータをコンマで 区切って指定します。初期化データがある場合はnew演算子は省略できます。
int[] a={0,1,2,3,4,5,6,7,8,9};
これでa[0]に0、a[1]に1,・・・a[9]に9が初期化されます。
3. drawLines メソッド
Mの文字を構成する4本の直線のそれぞれの始点x,y座標、終点x,y座標を配列p[ ]に格 納しておき、「canvas.drawLines(p,paint);」とするとMの文字が描画されます。
4. 座標移動
「canvas.translate(sx,sy);」とすることで座標原点を現在位置から(sx,sy)だけ移動しま す。
「例題2-9」Mの文字を座標を移動しながら20個描画します。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
float[] p =
{30,210,30,30,30,30,120,120,120,120,210,30,210,30,210,210};
for (int i=0;i<20;i++){
canvas.translate(3,3);
canvas.drawLines(p,paint);
} } }
「練習問題2-9」fruits[ ]配列に格納されている文字を描画しなさい。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setTextSize(40);
① fruits={"Apple","Orange","Strawberry"};
for (int i=0;i<3;i++){
canvas.drawText( ② ,10,(i+1)*40,paint);
} } }
2-10 2 次元配列
成績表のような表データは、行と列を要素とする縦横の表で、これは 2 次元配列で表現 することができます。2次元配列は次のように行要素と列要素を指定して宣言します。
int[][] m=new int[5][3];
↑↑列要素の個数 行要素の個数
上図の斜線部の要素はm[3][2]と表せます。2次元配列の宣言時に初期化要素を置くこと もできます。
int[][] m={{80,70,60}, {60,90,70}, {80,80,70}, {70,60,50}, {80,90,90}};
int[][] m=new int[5][3];という2次元配列の行要素数、列要素数はlengthプロパティー を使って以下のように取得できます。
m.length 行要素数の5を返す。
m[0].length 列要素数の3を返す。
「例題2-10」2×2のマスを示す2次元配列m[ ][ ]の要素が1なら白石、2なら黒石を描画 します。
private class GView extends View { private Bitmap white,black;
public GView(Context context) { super(context);
white=BitmapFactory.decodeResource(getResources(),R.drawable.white);
black=BitmapFactory.decodeResource(getResources(),R.drawable.black);
}
protected void onDraw(Canvas canvas) { int[][] m={{1,2},{2,1}};
int w=white.getWidth();
int h=white.getHeight();
for (int i=0;i<2;i++){
for (int j=0;j<2;j++){
if (m[i][j]==1)
canvas.drawBitmap(white,j*w,i*h+30,null);
else
canvas.drawBitmap(black,j*w,i*h+30,null);
} } } }
「練習問題2-10」8×8のマスを示す2次元配列m[ ][ ]の要素が1なら白石、2なら黒石、
0なら緑を描画しなさい。
private class GView extends View { private Bitmap white,black,green;
public GView(Context context) { super(context);
white=BitmapFactory.decodeResource(getResources(),R.drawable.white);
black=BitmapFactory.decodeResource(getResources(),R.drawable.black);
green=BitmapFactory.decodeResource(getResources(),R.drawable.green);
}
protected void onDraw(Canvas canvas) { int[][] m={
{0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,1,2,0,0,0}, {0,0,0,2,1,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}
};
int w=white.getWidth();
int h=white.getHeight();
for (int i=0;i<8;i++){
for (int j=0;j<8;j++){
if ( ① )
canvas.drawBitmap(white,j*w,i*h+30,null);
else if ( ② )
canvas.drawBitmap(black,j*w,i*h+30,null);
else
canvas.drawBitmap(green,j*w,i*h+30,null);
}
} } }
2-11 データ型
Javaで使用できる基本データ型として以下があります。
型名 サイズ 意味、値の範囲
byte 8ビット バイト整数。-128~127。
short 16ビット 短長整数。-32768~32767。
int 32ビット 自然長整数。-2147483648~2147483647。
long 64ビット 倍長整数。-9223372036854775808~9223372036854775807。
float 32ビット 単精度浮動小数点数。±3.4E-38~±3.4E+38。有効桁6~7。
double 64ビット 倍精度浮動小数点数。±1.79E-308~±1.79E+308。有効桁15。
char 16ビット 文字型。Unicode。
boolean 8ビット 論理型。true/false。
基本データ型は単純なデータ型ですが、クラスと呼ばれる複雑なデータ型があります。
以下はこの章で使用しているクラスです。クラス名の先頭は大文字になっています。
クラス 意味
Canvas 描画キャンバス。
Paint 描画を行うペン。
Bitmap ビットマップイメージ。
String 文字列。
「例題2-11」Bitmapクラスの配列m[ ][ ]にwhite、blackのBitmapデータを初期化し、
そのデータを元にイメージを描画します。
private class GView extends View { private Bitmap white,black;
public GView(Context context) { super(context);
white=BitmapFactory.decodeResource(getResources(), R.drawable.white);
black=BitmapFactory.decodeResource(getResources(), R.drawable.black);
}
protected void onDraw(Canvas canvas) {
Bitmap[][] m={{white,black},{black,white}};
int w=white.getWidth();
int h=white.getHeight();
for (int i=0;i<2;i++){
for (int j=0;j<2;j++){
canvas.drawBitmap(m[i][j],j*w,i*h+30,null);
} } } }
「練習問題2-11」Stringクラスの配列に"♂"、"♀"の文字列データを初期化し、そのデータ を描画します。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setTextSize(60);
String[][] m={{"♂","♀"},{"♀","♂"}};
for (int i=0;i<2;i++){
for (int j=0;j<2;j++){
canvas.drawText( ① ,(i+1)*60,paint);
} } } }
2-12 while 文
for 文は繰り返し回数が決まっている繰り返しですが、while文は繰り返し回数が決まっ ていない繰り返しです。( )内の式が真の間{ }内の文を繰り返します。
while (式) { 文 }
「例題2-12」Mを示す5点の座標データがx[ ]とy[ ]に格納されています。配列データの終
わりの印として「-1」を置いてあります。LPX,LPYには直線を描く際の始点データを格納 します。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
float[] x={30,30,120,210,210,-1};
float[] y={210,30,120,30,210,-1};
float LPX=x[0],LPY=y[0]; // 最初の点 int i=1; // 2番目の点から
while (x[i]!=-1){
canvas.drawLine(LPX,LPY,x[i],y[i],paint);
LPX=x[i];LPY=y[i];
i++;
} } }
「練習問題2-12」例題2-12ではiを1から始めていましたが、0から開始して、while文 の中でiが0でないときだけ直線を描くようにしなさい。LPX,LPYの初期化は必要です。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
float[] x={30,30,120,210,210,-1};
float[] y={210,30,120,30,210,-1};
float LPX=0,LPY=0;
int ①
while (x[i]!=-1){
if ( ② )
canvas.drawLine(LPX,LPY,x[i],y[i],paint);
LPX=x[i];LPY=y[i];
i++;
} } }
2-13 ユーザ定義メソッド
今までは各クラスですでに定義されているメソッドについて説明しましたが、ここでは ユーザ定義のメソッドを作ります。ユーザ定義メソッドの定義と呼び出しは次のように行 います。メソッドが呼び出し元にデータを返さない場合はメソッドの型を「void」とし
「return 式;」はありません。
メソッド名(実引数,実引数,・・・); // メソッドの呼び出し 型 メソッド名(型 仮引数, 型 仮引数,・・・) { // メソッドの定義
return 式;
}
「例題2-13」引数で与えられたテキストと個数の星を表示するユーザ定義メソッドstarを
作ります。starメソッドはCanvasオブジェクト(Canvas canva)、表示y座標(int y)、
表示する文字列(String msg)、表示する星の数(int n)を引数に持ちます。結果を返さ ないメソッドなのでvoid型とします。またアクセス属性はprivateとします。
private class GView extends View { private Paint paint;
private Bitmap star;
public GView(Context context) { super(context);
paint=new Paint();
star=BitmapFactory.decodeResource(getResources(),R.drawable.star);
}
protected void onDraw(Canvas canvas) { star(canvas,10,"価格:",3);
star(canvas,40,"品質:",5);
}
private void star(Canvas canvas,int y,String msg,int n){
paint.setColor(Color.BLUE);
paint.setTextSize(30);
canvas.drawText(msg,0,y+23, paint);
for (int i=0;i<n;i++)
canvas.drawBitmap(star,i*30+90,y,null);
} }
「練習問題2-13」starメソッドの引数に描画色を示す「Paint paint」引数を追加しなさい。
private class GView extends View { private Paint paint;
private Bitmap star;
public GView(Context context) { super(context);
paint=new Paint();
star=BitmapFactory.decodeResource(getResources(),R.drawable.star);
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setTextSize(30);
star(canvas,paint,10,"価格:",3);
paint.setColor(Color.RED);
star(canvas,paint,40,"品質:",5);
}
private void star(Canvas canvas, ① ,int y,String msg,int n){
canvas.drawText(msg,0,y+23, ② );
for (int i=0;i<n;i++)
canvas.drawBitmap(star,i*30+90,y,null);
} }
2-14 メンバ変数
メソッド内で宣言した変数(オブジェクト)はそのメソッド内でのみ使用できます。こ れをローカル変数と呼びます。複数のメソッドで共通に使う変数(オブジェクト)はクラ ス定義の先頭で、メソッドの外で宣言します。これをメンバ変数と呼びます。
private class クラス名 {
メンバ変数(オブジェクト)の宣言
private メソッド名1() { ローカル変数の宣言 }
private メソッド名2() { ローカル変数の宣言 }
}
メンバ変数のアクセス属性は通常「private」指定します。
private float LPX=0,LPY=0; // メンバ変数
private指定しなくてもprivateとして扱われますが、本書では「private」指定を明示し
ています。privateは外部に対し非公開、publicは外部に対し公開を意味します。ローカル 変数にはアクセス属性は指定しません。
「注」アクセス属性
メソッドやオブジェクトの使用権限を privateやpublic といったアクセス属性により指 定できます。
アクセス属性 意味
public どこからでも使用できる。
protected このメソッドまたはオブジェクトを定義しているクラスとサブクラスか
ら使用できる。
private このメソッドまたはオブジェクトを定義しているクラスのみから使用で
きる。
「注」paintはすでにメンバ変数として使用してきました。
「例題2-14」描画現在位置を移動するmoveToメソッドと現在位置から指定点まで直線を
描くlineToメソッドを作り、これらを使用してMの文字を描きます。現在位置を示す座標
のLPXとLPYをメンバ変数とします。
private class GView extends View { private Paint paint;
private float LPX=0,LPY=0; // メンバ変数 public GView(Context context) {
super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { float[] x={30,30,120,210,210,-1};
float[] y={210,30,120,30,210,-1};
int i=0;
while (x[i]!=-1){
if (i==0)
moveTo(x[i],y[i]);
else
lineTo(canvas,x[i],y[i]);
i++;
} }
private void moveTo(float x,float y){
LPX=x;LPY=y;
}
private void lineTo(Canvas canvas,float x,float y){
paint.setColor(Color.BLUE);
canvas.drawLine(LPX,LPY,x,y,paint);
LPX=x;LPY=y;
} }
「練習問題2-14」lineToメソッドのcanvas引数をメンバ変数で代替えしなさい。
private class GView extends View { private ①
private Paint paint;
private float LPX=0,LPY=0; // メンバ変数 public GView(Context context) {
super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { mCanvas= ②
paint.setColor(Color.BLUE);
float[] x={30,30,120,210,210,-1};
float[] y={210,30,120,30,210,-1};
int i=0;
while (x[i]!=-1){
if (i==0)
moveTo(x[i],y[i]);
else
lineTo(x[i],y[i]);
i++;
} }
private void moveTo(float x,float y){
LPX=x;LPY=y;
}
private void lineTo(float x,float y){
mCanvas.drawLine(LPX,LPY,x,y,paint);
LPX=x;LPY=y;
} }
2-15 配列引数
メソッドに配列引数を渡すには、実引数は配列名のみを指定し、仮引数は要素数を指定 しない配列宣言を指定します。たとえばのdispに一次元配列x[ ]を渡すには以下のように します。仮引数の名前は x でなく別の名前でもよいです。受け取った配列の要素数は
「x.length」で取得できます。
float[] x={30,30,120,210,210};
disp(x);
private void disp(float[] x){
}
「例題2-15」Mの各点の座標データのx[ ]とy[ ]をlinesメソッドに渡して描画します。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { float[] x={30,30,120,210,210};
float[] y={210,30,120,30,210};
paint.setColor(Color.BLUE);
lines(canvas,paint,x,y);
}
private void lines(Canvas canvas,Paint paint,float[] x,float[] y){
int n=x.length;
for (int i=0;i<n-1;i++){
canvas.drawLine(x[i],y[i],x[i+1],y[i+1],paint);
} } }
2 次元配列を渡すには以下のようにします。受け取った配列の行要素数は「p.length」、 列要素数は「p[0].length」で取得できます。
float[][] p={{30,210},{30,30},{120,120},{210,30},{210,210}};
disp(p);
private void disp(float[][] p){
}
「練習問題2-15」Mのデータを{x,y}を要素とする 2次元配列で表し、このデータをlines に渡して描画します。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) {
float[][] p={{30,210},{30,30},{120,120},{210,30},{210,210}};
paint.setColor(Color.BLUE);
lines(canvas,paint, ① );
}
private void lines(Canvas canvas,Paint paint, ② ){
int n=p.length;
for (int i=0;i<n-1;i++){
canvas.drawLine(p[i][0],p[i][1],p[i+1][0],p[i+1][1],paint);
} } }
2-16 Math クラス
Mathクラスは三角関数、指数関数、対数関数、平方根などの数値計算を行うためのJava の標準クラスです。java.langパッケージに含まれるためimport文は必要ありません。Math クラスのメソッドはstaticメソッドで構成されています。このため、個々のオブジェクト を生成せずにデフォルトで生成されいる静的なインスタンス(クラス名のインスタンス)
のMathに対して「Math.sin」や「Math.cos」のように使用します。
Mathクラスの主なstaticメソッドとして以下があります。
メソッド 機能
double sin(double x) xのサイン。
double cos(double x) xのコサイン。
double tan(double x) xのタンジェント。
double asin(double x) xのアークサイン。
double acos(double x) xのアークコサイン。
double atan(double x) xのアークタンジェント。
double exp(double x) xの指数。ex。
double sqrt(double x) xの平方根。√x。
double log(double x) xの自然対数。logex。
double pow(double x,double y) xの累乗。xy。 double IEEEremainder(double
x,double y)
x÷yの余り。
double ceil(double x) xを数直線上で右方向へ整数化した値を返す。切り上げ。
double floor(double x) xを数直線上で左方向へ整数化した値を返す。切り下げ。
double round(double x) xを四捨五入した値を返す。
double random() 0.0~1.0未満の乱数を1つ返す。
PI πの値。PIはメソッドでなくフィールド。
1. 三角関数とラジアン
三角関数のsin,cos,tan,asin,acos,atanに与えるデータは度でなくラジアンという単位を 使います。ラジアンという単位は、半径1の単位円において、角度に対応する円弧の長さ を指します。半径1の円の180°がπラジアンに相当しますので、x°をラジアンに直すに は「x*Math.PI/180」とします。
「例題2-16-1」(150,150)を中心に半径120の円周を想定します。この円周上を20°ごとに 取った点と中心を直線で結びます。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
float x,y;
for (int i=0;i<360;i+=20){
x=(float)(120*Math.cos(i*Math.PI/180)+150);
y=(float)(120*Math.sin(i*Math.PI/180)+150);
canvas.drawLine(150,150,x,y,paint);
} } }
「練習問題2-16-1」y=sin(x)のサインカーブを描きなさい。xを0°~360°まで5°おきに 変化させたときの(x,y)の各点を直線で結びます。sinの値は-1.0~1.0の範囲なので120倍 した値をy座標とします。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
float x,y,LPX=0,LPY=0;
for (x=0;x<=360;x+=5){
y=(float)(150-120*Math.sin(x*Math.PI/180));
if ( ① )
canvas.drawLine( ② ,paint);
LPX=x;LPY=y;
} } }
2. 乱数
Math.random()は0.0~1.0未満の乱数を1つ返します。0~400未満の乱数をxに取得す
るには以下のようにします。random()が返す値はdouble型なので、float型のxに代入す るには(float)でキャストする必要があります。
x=(float)(400*Math.random());
「注」キャスト
キャストとはオペランドの型を、それが評価されるときだけ一時的に変えることをいい ます。キャストは一時的に変えたい型を()で囲んでオペランドの前に指定します。
(型)オペランド
「例題 2-16-2」乱数で円の中心位置と半径を求めて、30 個の円を描画します。円は
drawCircleメソッドで描画します。paint.setStyle(Style.STROKE);を設定しなければ円の 中が塗られます。Style.STROKEを使用するため「import android.graphics.Paint.Style;」
が必要です。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setStyle(Style.STROKE);
float x,y,r;
for (int i=0;i<30;i++){
x=(float)(400*Math.random());
y=(float)(400*Math.random());
r=(float)(50*Math.random());
canvas.drawCircle(x,y,r,paint);
} } }
「練習問題2-16-2」中心(230,230)、半径160の円周上に20°おきに乱数で半径を求めた円 を描きなさい。「import android.graphics.Paint.Style;」が必要です。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setStyle(Style.STROKE);
float x,y,r;
for (int i=0;i<360;i+=20){
x=(float)(160* ① +230);
y=(float)(160* ② +230);
r=(float)(50*Math.random());
canvas.drawCircle(x,y,r,paint);
} } }
☆応用サンプル ダイアモンドリング
半径rの円の円周をN等分し、各点をすべて結ぶとダイアモンドリングと呼ばれる図形 が得られます。円周上のi番目の点の座標は次式で求められます。
x1=(float)(cx+r*Math.cos(i*2*Math.PI/N)); // 始点 y1=(float)(cy-r*Math.sin(i*2*Math.PI/N));
N等分したときの角は2π/Nとなります。cx,cyは円の中心座標とします。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { int N=16; // 分割数
float x1,y1,x2,y2,cx=200,cy=200,r=180;
paint.setColor(Color.BLUE);
for (int i=0;i<N-1;i++){
x1=(float)(cx+r*Math.cos(i*2*Math.PI/N)); // 始点 y1=(float)(cy-r*Math.sin(i*2*Math.PI/N));
for (int j=i+1;j<N;j++){
x2=(float)(cx+r*Math.cos(j*2*Math.PI/N)); // 終点 y2=(float)(cy-r*Math.sin(j*2*Math.PI/N));
canvas.drawLine(x1,y1,x2,y2,paint);
} } } }
☆応用サンプル モンテカルロ法によるπ
0~1の一様乱数を2つ発生させ、それらをx,y とします。こうした乱数の組をいくつか 発生させると、1×1 の正方形の中に(x,y)で示される点は均一にばらまかれると考えられま す。したがって、正方形の面積と1/4円の面積の比は、そこにばら撒かれた乱数の数に比 例するはずです。今、1/4 円の中にばらまかれた乱数の数を a、円外にばらまかれた乱数 の数をbとすると、次の関係が成立します。
π/4:1=a:a+b
∴π=4a/(a+b)=4a/n nは発生した乱数の総数。
「import android.graphics.Paint.Style;」が必要です。
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
paint.setStyle(Style.STROKE);
int sum=0,N=5000;
float x,y,pai;
canvas.drawRect(0,10,150,160, paint);
for (int i=0;i<N;i++){
x=(float)Math.random();
y=(float)Math.random();
if (x*x+y*y<1){
sum++;
canvas.drawPoint(150*x,160-150*y, paint);
} }
pai=(float)4*sum/N;
paint.setTextSize(30);
canvas.drawText("π="+pai,0,200,paint);
} }
☆応用サンプル タートルグラフィックス
長さと角度を与えて直線を引くメソッドmoveと現在角を回転するメソッドturnを作り ます。このようなmoveやturnを使って描く図形をタートル・グラフィックスと呼びます。
・moveメソッド
描画の現在位置を(LPX,LPY)、現在角をANGLEとすると、そこから長さlengの直 線を引く場合の終点座標(x,y)は以下の計算式から得られます。ANGLE度をラジアンに変換 するために「Math.PI/180」を乗じます。cos、sinはMathクラスのstaticメソッドです。
float x=LPX+(float)(leng*Math.cos(ANGLE*Math.PI/180));
float y=LPY-(float)(leng*Math.sin(ANGLE*Math.PI/180));
・turnメソッド
現在角ANGLEをa°回転します。ANGLEの値が0~360°に入るように
ANGLE=Math.IEEEremainder(ANGLE,360.0);
という式で補正します。
private class GView extends View { private Paint paint;
private float LPX,LPY,ANGLE;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { paint.setColor(Color.BLUE);
for (int n=3;n<=9;n++){
LPX=180;LPY=400;ANGLE=0;
for (int i=1;i<=n;i++){
move(canvas,120f,paint);
turn(360.0f/n);
} }
}
private void move(Canvas canvas,float leng,Paint paint) { float x=(float)(LPX+leng*Math.cos(ANGLE*Math.PI/180));
float y=(float)(LPY-leng*Math.sin(ANGLE*Math.PI/180));
canvas.drawLine(LPX,LPY,x,y, paint);
LPX=x;LPY=y;
}
private void turn(float a) { ANGLE+=a;
ANGLE=(float)Math.IEEEremainder(ANGLE,360.0);
} }