fc2ブログ

戌印-INUJIRUSHI- (Androidあれこれ)

Androidのプログラミングをメインにしてます。記事に貼られたソースコードはダブルクリックすることで行番号をはずしてコピーすることができます。

 
1
2
3
4
5
7
8
10
11
12
13
14
15
17
18
19
20
21
22
23
25
26
27
28
29
30
06

Matrixを使って画像を拡大・縮小する

Canvas 等に画像を描画するとき、画像の大きさを変えたいときがあると思います。
そのときには Matrix を使うことで画像を拡大・縮小することができます。

API Reference: SurfaceView

■ 画面イメージ -----------------------------------------------
android_Matrix_Scale.png

■ ソースコードで定義 -----------------------------------------
// 縦横のスケールを取得する(1以上であればCanvasの方が大きい)
float fw = (float) getWidth() / (float) bitmap.getWidth();
float fh = (float) getHeight() / (float) bitmap.getHeight();

// スケールの小さい方を取得する
float min = Math.min(fw, fh);

// Matrixを生成する
Matrix matrix = new Matrix();

// スケールを設定する
// 縦横の比率を変えないために同じ値(小さい方)を設定する
matrix.postScale(min, min);

// Matrixを適用したBitmapを生成する
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);

スポンサーサイト



SurfaceViewとダブルバッファリング

参考サイト:Waku Waku Java - 講座1 ダブルバッファリング

ダブルバッファリングとは、画面のちらつきを防ぐための技術です。

一般的な描画は、画面を一度クリア(もしくは塗りつぶし)してから背景、キャラクターと描いていきます。
キャラクターを動かしたりなどのアニメーションを行うときはコレを繰り返し行います。

通常の描画の場合、画面に直接描画を行っています。
そのため、画面をクリアした際に何も無い画面が一瞬見えてしまうために画面がちらついて見えてしまうことがあります。

そこでダブルバッファリングの出番です。
ダブルバッファリングでは、表示されている画面とは別に、裏にもう一つの画面(オフスクリーン・バッファ)を持ちます。
裏の画面で描画を行い、描画が完成したら表の画面と入れ替えることで描画している姿を見せないようにしています。
これによりクリアされた画面が見えないためにちらつきを防ぐことができます。

図1:
double_buffe.png


さて、ここから SurfaceView の話。
(正確にはダブルバッファリングの使い方、かな?)
【関連記事】描画Viewクラス : SurfaceView

SurfaceViewを使っているのに描画がちらついて見える、という方がいると思います。
ダブルバッファリングを行っているんだからちらつかないよね?と思う方もいると思いますが、これはCanvasが2つ存在していることに原因があります。

2つのCanvasは独立しており、片方のCanvasに描画した内容は、もう片方のCanvasに反映されません。

毎回1から描画をする場合には問題にならないことですが、お絵かきツールのようにCanvasに追記していく場合に問題になります。

たとえば、先ほどの神父さんが道を歩いていたら、突然人に襲われたとします。
最初は神父さん一人だけでしたが、後で襲撃者を追記します。
この場合、SurfaceViewで普通に描くと図2のようになります。

図2:
double_buffe2.png

最初のCanvas(以下、Canvas1)には神父さんがいますが、追記された襲撃者は別のCanvas(以下、Canvas2)に描画されているためこのような現象が発生します。
スレッドなどで描画を繰り返している場合、別々の絵が描かれたCanvas1とCanvas2が交互に表示されるためにちらつきが発生してしまいます。


この問題の対策としては、2つのCanvasに共通するバッファを持つことです。
共通するバッファ(ここではBitmap)に描画を行い、最後にバッファを対象となるCanvasに描きだす(図3)。
これでCanvas1とCanvas2の差はなくなり、ちらつきがなくなります。
(Canvas2 には Canvas1 + 追記分)

図3:
double_buffe3.png

以上で、SurfaceViewとダブルバッファリングの説明は終了です。

最後に、追記型描画のちらつき対応を行ったサンプルソースコードを載せます。
このサンプルでは1~10の数値を画面のランダム位置に描画します。
(図と関係ないプログラム)
package jp.inujirushi.sample.activity;

import java.util.Random;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class SurfaceViewActivity extends Activity {

/** アクティビティを生成した時に呼ばれます。 */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleSurfaceView(this));
}

/**
* SurfaceView 拡張クラス
*/
class SampleSurfaceView extends SurfaceView implements
SurfaceHolder.Callback, Runnable {
// 描画情報
SurfaceHolder mHolder;
Thread mThread;
Bitmap mBitmap;

/**
* コンストラクタ
*
* @param context
*/
public SampleSurfaceView(Context context) {
super(context);

// Holderを取得する
mHolder = getHolder();

// SurfaceViewにコールバックを追加(開始)する
mHolder.addCallback(this);
}

/**
* SurfaceViewが生成されたときに呼ばれます。
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
// オフスクリーン用のBitmapを生成する
mBitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Config.ARGB_8888);

// スレッドを開始する
mThread = new Thread(this);
mThread.start();
}

/**
* SurfaceViewの状態が変更されたときに呼ばれます。
* SurfaceViewが生成されたときにも呼ばれます。
*/
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}

/**
* SurfaceViewが破棄されるときに呼ばれます。
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// スレッドを破棄する
mThread = null;

// Bitmapを解放する
if (mBitmap != null)
mBitmap.recycle();
}

/**
* スレッド処理を行います
*/
@Override
public void run() {
// メイン処理(1~10まで描画)
for (int i = 1; i <= 10; i++) {
// Canvasをロックして取得する
Canvas canvas = mHolder.lockCanvas();

// オフスクリーンバッファに描画する
if (mBitmap != null) {
// オフスクリーンバッファを生成する
Canvas offScreen = new Canvas(mBitmap);

// オフスクリーンバッファに書き込む
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(30);
int x = new Random().nextInt(100) * (getWidth() / 100);
int y = new Random().nextInt(100) * (getHeight() / 100);
offScreen.drawText("" + i, x, y, paint);
}

// オフスクリーンバッファを描画する
canvas.drawBitmap(mBitmap, 0, 0, null);

// Canvasのロックを解除する
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
※107行目でオフスクリーン・バッファに対して描画を行っています。
 これをロックしたキャンバス(94行目)に対して行った場合、偶数しか表示されなくなります。

描画Viewクラス : SurfaceView

SurfaceView は描画を行うViewクラスのひとつです。

ビュー(ウィジェット)はアプリケーションのメインスレッド(UIスレッド)で描画が行われていますが SurfaceView は描画スレッドが独立しているため、メインスレッドで処理をしながら描画を行うことができます。

そのため、計算と描画を同時に行う必要のあるゲームプログラミング等で使われています。

※ SurfaceView はダブルバッファリングが行われています。使用する際には注意してください。
【関連記事】SurfaceViewとダブルバッファリング

API Reference:
SurfaceView

ソフトウェア技術ドキュメントを勝手に翻訳
Android 開発ガイド‎ - ‎フレームワークトピック‎ - b. グラフィックス

■ 画面イメージ -----------------------------------------------
android_SurfaceView.png
# 動きが無いのでImageViewとの違いがわからないですね…

■ ソースコード -----------------------------------------------
package jp.inujirushi.sample.view;

import jp.inujirushi.sample.R;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class SampleSurfaceView extends SurfaceView implements
SurfaceHolder.Callback {

/**
* コンストラクタ
*
* @param context
*/
public SampleSurfaceView(Context context) {
super(context);

// SurfaceViewにコールバックを追加(開始)する
getHolder().addCallback(this);
}

/**
* コンストラクタ
* XMLから呼び出すときには AttributeSet を含むコンストラクタを実装する必要があります
*
* @param context
* @param attrs
*/
public SampleSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);

// SurfaceViewにコールバックを追加(開始)する
getHolder().addCallback(this);
}

/**
* SurfaceViewが生成されたときに呼ばれます
*
* @param holder
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
// Canvasをロックして取得する
Canvas canvas = holder.lockCanvas();

// Canvasに描画する
Bitmap bitmap = BitmapFactory.decodeResource(
getResources(), R.drawable.icon);
canvas.drawBitmap(bitmap, 0, 0, null);
canvas.drawBitmap(bitmap, 50, 50, null);
canvas.drawBitmap(bitmap, 100, 100, null);
bitmap.recycle();

// Canvasのロックを解除する
holder.unlockCanvasAndPost(canvas);
}

/**
* SurfaceViewの状態が変更されたときに呼ばれます。
* SurfaceViewが生成されたときにも呼ばれます。
*
* @param holder
* @param format
* @param width
* @param height
*/
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}

/**
* SurfaceViewが破棄されるときに呼ばれます
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}


■ 使い方 -----------------------------------------------------
res/layout/sample_surface.xml (XMLを使用しないのであれば不要)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<jp.inujirushi.sample.view.SampleSurfaceView
xmlns:app="http://schemas.android.com/apk/res/jp.inujirushi.sample.view"
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
独自Viewを使用しているため、タグ名、および、xmlns:app にパッケージを記述する必要があります。


Javaソースコード
package jp.inujirushi.sample.activity;

import jp.inujirushi.sample.R;
import android.app.Activity;
import android.os.Bundle;

public class SurfaceViewActivity extends Activity {

/** アクティビティを生成した時に呼ばれます。 */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_surface);

// XMLに定義しない場合はnewすればOK
//setContentView(new SampleSurfaceView(this));

// SurfaceViewを取得するには findViewById を使う
//SampleSurfaceView view = (SampleSurfaceView) findViewById(R.id.surfaceView);
}
}

Android 4.x 続々…

各キャリアの夏モデルが発表されましたね。

いままで Android 2.x が主流で、Android 4.x は各キャリアに1つあるかないかでしたが夏モデルからはほぼ全て Android 4.x に。

と言っても、これから出るのであってまだ1~2年(2年縛りがあるので)は 2.x を使うユーザーが大半を占めていると思います。
なので Android 4.x ベースで開発を進めるのはまだ少し早そうです。

このブログではまだまだ Android 2.2 をベースに記事を書いていきます。
(持ってる機種はAndroid 4.0 ですが…)

しかしバージョンアップによる改善や拡張は無視できないもの。
グーグルさんが推奨している Fragment 対応だけは早めにやっていこうと思います
(簡単な記事は書いてありますが、もうすこし突っ込んだ内容で…)

Fragment は Android 3.0 (Honeycomb) から追加された機能ですが、Android 3.0 以前でも(似たような機能が)使えるように Compatibility package が出されています。
※Android SDK Manager から取得できます

なので Android 2.2 ベースでも範囲内の内容ですね。
とはいえ全く使ったことがない…

Fragment に Compatibility package …まずは勉強から…
プロフィール

とむ・やむくん

Author:とむ・やむくん
管理人について

Windows 7 / 64bit
Eclipse 4.2 Juno (日本語パッチ済)

スポンサーサイト
最新トラックバック
検索フォーム
ブロとも申請フォーム
QRコード
QR
Twitter
2013/01/04 19:00 カウント開始