2011-10-28 2 次元配列を返り値に持つ関数 ================================================================================ サンプルコードは C++ 言語で書かれているが、C 言語でも同じ。 サンプルの関数 addTo() は、2 次元配列(正方行列)に対して、擬似コード的に記すと a += b; を行う関数。 □ sample.cpp --- #include <iostream> #include <iomanip> using namespace std; int (*addTo(int a[][2], int b[][2], int size))[2]; // 2次元配列を加算する (1) void print(int a[][2], int size); int main() { int a[2][2] = {{1, 2}, {3, 4}}; int b[2][2] = {{5, 6}, {7, 8}}; print(a, 2); cout << "--------" << endl; print(b, 2); int (*c)[2] = addTo(a, b, 2); // (2) cout << "========" << endl; print(c, 2); return 0; } int (*addTo(int a[][2], int b[][2], int size))[2] { for (int i = 0; i < size; ++i) { for (int j = 0; j < size; ++j) { a[i][j] += b[i][j]; } } return a; } void print(int a[][2], int size) { for (int i = 0; i < size; ++i) { for (int j = 0; j < size; ++j) { cout << setw(4) << a[i][j]; } cout << endl; } } --- □ 実行結果 --- 1 2 3 4 -------- 5 6 7 8 ======== 6 8 10 12 --- (1) 2 次元配列を返す関数のプロトタイプ宣言 int (*addTo(int a[][2], int b[][2], int size))[2]; // 2次元配列を加算する 通常の2次元配列を指すポインター変数の定義はつぎの(2)のように、 int (*c)[2] のようになる。この場合のポインター c は、int[2] のサイズを持つ型を指すことになる ので、結果として 2 次元配列へのポインターと考えられる。 こう考えると、返り値として int[2] を持つ関数としては、 int (*x)[2] のようになり、x 部分が関数のシグネチャー部分とするとよい。すなわち、上記のような 表現になる。 (2) 2 次元配列の宣言 int (*c)[2] = addTo(a, b, 2); // (2) ■ 関数ポインターにしてみる 次のようにできる。 関数名の部分を (*関数ポインター名) に置き換えるだけである。 ただし、これだとなんだかよくわからなくなるので、あまり記述しないほうがよさそう。 それ以前に、2 次元配列を返す関数を作るくらいなら、構造体に 2 次元配列を入れて扱 ったほうが記述としてはよいと思う。 --- : int (*(*fp)(int a[][2], int b[][2], int size))[2]; // 関数ポインターを定 義する fp = addTo; // 関数ポインターにアドレスを設定する int (*c)[2] = fp(a, b, 2); : ---
最終更新 2012-01-13
グラフィックス (1) ================================================================================ 画面に図形を描画する。 ここでは、コンテント・ビューとして res/layout を使うのではなく、クラスを使って行 う。 基本的なクラスのつくりは以下のとおり。 ・描画するクラスは、View クラスを継承して作る ・onDraw(Canvas canvas) をオーバーライドして、そこに描画する ・描画するメソッドは Canvas クラスのメソッドを使う ・描画に使う図形の属性は Paint オブジェクトを使う ■ 円、文字列、ビットマップ画像を描画する 円や文字列、ビットマップ画像を描画するには、View のサブクラスを作り、そのオブジ ェクトを Activity に設定する。 つぎの Graphics01Activity.java は、プロジェクトによりクラス名が違うだけで、すべ て共通で使用している。 □ Graphics01Activity.java --- package jp.marunomaruno.android.sample; import android.app.Activity; import android.os.Bundle; public class Graphics01Activity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GraphicsView v = new GraphicsView(this); // (1) setContentView(v); // (2) } } --- (1)(2) グラフィックスを設定しているクラスのオブジェクトを生成して、それをアクティビティに設定する。 GraphicsView v = new GraphicsView(this); // (1) setContentView(v); // (2) 自分で作ったビューを設定するので、今回は、res/layout/main.xml を使うことはない。 □ GraphicsView.java --- package jp.marunomaruno.android.sample; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.View; /** * 円、文字列、ビットマップ画像を描画する。 * @author marunomaruno * @see Graphics01Activity */ public class GraphicsView extends View { // (1) public GraphicsView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public GraphicsView(Context context, AttributeSet attrs) { super(context, attrs); } public GraphicsView(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { // (2) // ペイントオブジェクトを設定する Paint paint = new Paint(); // (3) paint.setColor(Color.WHITE); // (4) paint.setAntiAlias(true); // (5) // 円を描画する paint.setColor(Color.BLUE); canvas.drawCircle(100, 100, 50, paint); // (6) // 文字列を描画する paint.setColor(Color.YELLOW); paint.setTextSize(48); // (7) canvas.drawText("アイスクリーム", 50, 300, paint); // (8) // ビットマップ画像を取得する Bitmap bitmap = BitmapFactory.decodeResource( getResources(), R.drawable.icon); // (9) // 画像を描画する canvas.drawBitmap(bitmap, 50, 400, paint); // (10) } } --- (1) GraphicsView クラスを定義する public class GraphicsView extends View { // (1) View クラスを継承して作る。View クラスは、独自に図形などを描画するクラスを作ると きに使用するクラス。描画する場合は、onDraw(Canvas) メソッドをオーバーライドする。 □ View クラス 画面に表示やレイアウトするクラスのスーパークラス。 ・クラス階層 java.lang.Object + android.view.View ・主なメソッド --- void onDraw(Canvas canvas) 描画が行われるときに呼び出される --- (2) 描画するためのメソッドをオーバーライドする protected void onDraw(Canvas canvas) { // (2) このメソッドに、描画したい要素(図形、文字列、画像)を設定する。 □ Canvas クラス 描画対象の図形を描画して置いておくオブジェクトのクラス。 このクラスのメソッドを使って、図形、文字列、画像を描画する。 このとき、描画に使うペンの役割を、Paint オブジェクトが担当する。 ・クラス階層 java.lang.Object + android.graphics.Canvas ・主なメソッド --- void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 円弧 void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 画像 void drawCircle(float cx, float cy, float radius, Paint paint) 円 void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) 線分 void drawLines(float[] pts, Paint paint) いくつかの線分 void drawOval(RectF oval, Paint paint) 楕円 void drawPath(Path path, Paint paint) 多角形 void drawPoint(float x, float y, Paint paint) 点 void drawPoints(float[] pts, Paint paint) いくつかの点 void drawRect(float left, float top, float right, float bottom, Paint paint) 四角形 void drawText(String text, float x, float y, Paint paint) 文字列 --- (3)-(5) ペイントオブジェクトを設定する Paint paint = new Paint(); // (3) paint.setColor(Color.WHITE); // (4) paint.setAntiAlias(true); // (5) setColor() メソッドは、ペイントの色を設定する。 色は、Color クラス(android.graphics.Color)の定数を使用する。つぎの定数がある。 --- int BLACK int BLUE int CYAN int DKGRAY int GRAY int GREEN int LTGRAY int MAGENTA int RED int TRANSPARENT int WHITE int YELLOW --- これ以外の色については、RGB を直接指定する。次のような、rgb() メソッドを利用する。 paint.setColor(Color.rgb(red, green, blue)); □ Paint クラス Paint クラスは、図形描画する際のペンの役割を持つ。 ここで、ペンの色や太さを設定して、描画するときに指定する。 ・クラス階層 java.lang.Object + android.graphics.Paint ・内部クラス --- enum Paint.Align drawText() での文字列の境界 enum Paint.Cap 線分の端の形状 class Paint.FontMetrics 文字列の情報 class Paint.FontMetricsInt 文字列の情報 enum Paint.Join 線と線の結合具合 enum Paint.Style 線分のみか内部も塗りつぶすかの情報 --- ・主なメソッド --- void set(Paint src) void setARGB(int a, int r, int g, int b) void setAlpha(int a) void setAntiAlias(boolean aa) void setColor(int color) --- (6) 円を描画する canvas.drawCircle(100, 100, 50, paint); // (6) 円を描画するのは、Canvas クラスの drawCircle() メソッドを使う。 void drawCircle(float cx, float cy, float radius, Paint paint) AWT の描画するメソッドとは違い、中心 (cx, cy) と半径 radius を指定する。 また、塗りつぶすかどうかも、paint オブジェクトがその情報を持つので、メソッドを切 り替える必要はない。 (7)(8) 文字列を描画する paint.setTextSize(48); // (7) canvas.drawText("アイスクリーム", 50, 300, paint); // (8) 文字列を描画するのは、Canvas クラスの drawText() メソッドを使う。 void drawText(String text, float x, float y, Paint paint) 文字列の開始点は、座標 (x, y) からで、ここが文字列の下になる。 ・文字列の属性を設定する関係の Paint クラスのメソッド --- void setStrokeCap(Paint.Cap cap) void setStrokeJoin(Paint.Join join) void setStrokeMiter(float miter) void setStrokeWidth(float width) void setStyle(Paint.Style style) void setSubpixelText(boolean subpixelText) void setTextAlign(Paint.Align align) void setTextScaleX(float scaleX) void setTextSize(float textSize) void setTextSkewX(float skewX) Typeface setTypeface(Typeface typeface) void setUnderlineText(boolean underlineText) --- (9) ビットマップ画像を取得する Bitmap bitmap = BitmapFactory.decodeResource( getResources(), R.drawable.icon); // (9) 画像を取得するには、BitmapFactory.decodeResource() メソッドを使う。 static Bitmap decodeResource(Resources res, int id) id は、res/drawable フォルダに入れた画像ファイルのリソース ID。 R.drawable.icon の場合、画像ファイルは、 res/drawable-Xdpi/icon.png になる。ただし、拡張子は .png とは限らず、 .jpg や .gif などもある。 (10) 画像を描画する canvas.drawBitmap(bitmap, 50, 400, paint); // (10) 画像を描画するのは、Canvas クラスの drawBitmap() メソッドを使う。 void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 画像の左上の座標が、(left, top) となる。 ■ いろいろな図形を描画する 基本的な画像の描画方法がわかったところで、その他の Canvas クラスのメソッドを使っ て、図形を描画してみる。 また、このサンプルでは、座標がわかりやすいように、50ピクセルごとに格子も描画する。 格子は、単なる直線を組み合わせている。 □ GraphicsView.java --- package jp.marunomaruno.android.sample; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Cap; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.RectF; import android.view.Display; import android.view.View; import android.view.WindowManager; /** * いろいろな図形を描画する。 * @author marunomaruno * @see Graphics02Activity */ public class GraphicsView extends View { public GraphicsView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public GraphicsView(Context context, AttributeSet attrs) { super(context, attrs); } public GraphicsView(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { // 画面のサイズを取得する WindowManager manager = (WindowManager) getContext() .getSystemService(Context.WINDOW_SERVICE); // (1) Display display = manager.getDefaultDisplay(); // (2) int width = display.getWidth(); // (3) int height = display.getHeight(); // (4) // ペイントオブジェクトを設定する Paint paint = new Paint(); paint.setColor(Color.WHITE); paint.setAntiAlias(true); // 文字列を描画する paint.setTextSize(24); canvas.drawText("画面はたてにしてください。", 50, 50, paint); // 格子を描画する paint.setTextSize(20); paint.setStrokeWidth(1); // (5) paint.setColor(Color.WHITE); for (int i = 0; i < Math.max(width, height); i += 50) { canvas.drawText(Integer.toString(i), i, paint.getTextSize(), paint);// (6) canvas.drawLine(i, 0, i, height, paint); // (7) canvas.drawText(Integer.toString(i), 0, i, paint); canvas.drawLine(0, i, width, i, paint); } // 点を描画する paint.setStrokeWidth(10); canvas.drawPoint(50, 100, paint); // (8) // 線を描画する paint.setColor(Color.rgb(255, 0, 0)); canvas.drawLine(50, 150, 150, 150, paint); paint.setColor(Color.rgb(0, 255, 0)); paint.setStrokeCap(Cap.ROUND); // (9) canvas.drawLine(200, 150, 300, 150, paint); // 四角形を描画する paint.setColor(Color.rgb(0, 0, 255)); paint.setStyle(Style.FILL); // (10) canvas.drawRect(50, 200, 150, 300, paint); // (11) paint.setColor(Color.RED); paint.setStyle(Style.STROKE); canvas.drawRect(200, 200, 300, 300, paint); paint.setColor(Color.GREEN); paint.setStyle(Style.FILL_AND_STROKE); canvas.drawRect(350, 200, 450, 300, paint); // 円を描画する paint.setColor(Color.BLUE); paint.setStyle(Style.FILL); canvas.drawCircle(100, 400, 50, paint); // 1/4 円を描画する paint.setColor(Color.CYAN); paint.setStyle(Style.STROKE); RectF r1 = new RectF(200, 350, 300, 450); // (12) canvas.drawArc(r1, 0, 90, true, paint); // (13) paint.setColor(Color.MAGENTA); paint.setStyle(Style.FILL_AND_STROKE); RectF r2 = new RectF(350, 350, 450, 450); canvas.drawArc(r2, 180, 90, false, paint); // 三角形を描画する paint.setColor(Color.GRAY); paint.setStyle(Style.FILL); Path path = new Path(); // (14) path.moveTo(100, 500); // (15) path.lineTo(50, 600); // (16) path.lineTo(150, 600); path.lineTo(100, 500); canvas.drawPath(path, paint); // (17) } } --- □ 画面のサイズを取得する WindowManager manager = (WindowManager) getContext() .getSystemService(Context.WINDOW_SERVICE); // (1) Display display = manager.getDefaultDisplay(); // (2) int width = display.getWidth(); // (3) int height = display.getHeight(); // (4) 画面に格子を描画するため、画面の幅、高さを取得する。そのための画面のサイズは、 Display オブジェクトが持つ。 Display オブジェクトは、WindowManager クラスの getDefaultDisplay() メソッドを使って取得する。 画面の幅、高さは、それぞれ、Display クラスの getWidth() メソッド、getHeight() メソッドで取得する。 □ 格子を描画する paint.setTextSize(20); paint.setStrokeWidth(1); // (5) paint.setColor(Color.WHITE); for (int i = 0; i < Math.max(width, height); i += 50) { canvas.drawText(Integer.toString(i), i, paint.getTextSize(), paint); // (6) canvas.drawLine(i, 0, i, height, paint); // (7) canvas.drawText(Integer.toString(i), 0, i, paint); canvas.drawLine(0, i, width, i, paint); } 画面のサイズが取得できれば、それを基に、格子を描画する。格子は50ピクセルごと。 実機がたて、横どちらでも対応できるように、最大値でループする。はみ出した分は描画 されない。 (6)は、座標の値を描画する。左端が i で、テキストの長さは、paint オブジェクトから 取得する。 (7)は、格子になる直線を描画している。x 座標が同じなので、これはたての線を描画す る。 □ 点を描画する paint.setStrokeWidth(10); canvas.drawPoint(50, 100, paint); // (8) 座標 (x, y) に点を描画する。 void drawPoint(float x, float y, Paint paint) □ 線を描画する paint.setColor(Color.rgb(255, 0, 0)); canvas.drawLine(50, 150, 150, 150, paint); paint.setColor(Color.rgb(0, 255, 0)); paint.setStrokeCap(Cap.ROUND); // (9) canvas.drawLine(200, 150, 300, 150, paint); 座標 (startX, startY) から座標 (stopX, stopY) への直線を描画する。 void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) □ 四角形を描画する paint.setColor(Color.rgb(0, 0, 255)); paint.setStyle(Style.FILL); // (10) canvas.drawRect(50, 200, 150, 300, paint); // (11) 四角形の左上の座標 (left, top) と、右下の座標 (right, bottom) を指定して、四角形 を描画する。 void drawRect(float left, float top, float right, float bottom, Paint paint) □ 1/4 円を描画する paint.setColor(Color.CYAN); paint.setStyle(Style.STROKE); RectF r1 = new RectF(200, 350, 300, 450); // (12) canvas.drawArc(r1, 0, 90, true, paint); // (13) 四角形 oval に内接する楕円で、startAngle 度の位置から sweepAngle 度分の円弧を描 く。角度の単位は度(degree)。0度は、X 軸の正方向。Y 軸の正方向に向かう。したがっ て、90 度の指定は、右下に弧がある図形になる。 void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) このとき、useCenter がtrue であれば、中心を通る円弧(扇形)。そうでなければ、2 つ の頂点間で直線を引く。 □ RectF クラス 四角形のクラス。このオブジェクトに内接する形で、楕円などを描くことができる。 ・クラス階層 java.lang.Object + android.graphics.RectF ・主なインスタンス変数 --- public float bottom public float left public float right public float top --- ・主なコンストラクター --- RectF() RectF(float left, float top, float right, float bottom) RectF(RectF r) RectF(Rect r) --- ・主なメソッド --- boolean contains(float left, float top, float right, float bottom) boolean contains(float x, float y) boolean contains(RectF r) final float height() void set(float left, float top, float right, float bottom) void set(RectF src) void set(Rect src) final float width() --- □ 三角形を描画する paint.setColor(Color.GRAY); paint.setStyle(Style.FILL); Path path = new Path(); // (14) path.moveTo(100, 500); // (15) path.lineTo(50, 600); // (16) path.lineTo(150, 600); path.lineTo(100, 500); canvas.drawPath(path, paint); // (17) path で指定した点を頂点とする図形を描画する。 void drawPath(Path path, Paint paint) 多角形を描画するときには、その頂点の座標を指定する。 そして、その座標どおしを結ぶ線として、多角形を描画する。 座標を結ぶ線としては、Path クラスのオブジェクトを使う。 moveTo() で起点の座標を指定し、lineTo() で、そこから線で結ぶ次の座標を指定する。 □ Pathクラス Path クラスは、図形の輪郭を作る線分を抽象化しているクラス。これを使うことで多角 形を描くことができる。 ・クラス階層 java.lang.Object + android.graphics.Path ・コンストラクター --- Path() Path(Path src) --- ・主なメソッド --- void lineTo(float x, float y) void moveTo(float x, float y) --- 更新 内容 2012-01-13 GraphicsView クラスにコンストラクター追加