WebViewに独自ヘッダーを埋め込む

WebViewで、独自の情報をヘッダーに埋め込みます。
自分の調査不足なのか判りませんが、独自ヘッダーが有効なのは、埋め込んだ直後1ページのみで、そこから別のページに遷移するとヘッダー情報は失われました。
ヘッダー情報を保持し続ける方法をご存知の方がいらっしゃいましたら、コメントくださいm(__)m

public class WebViewActivity extends Activity {
	private String DEFAULT_URL	= "○○○.html";
	private String CHECK_URL	= "△△△.html";

	private JSONObject kv;
	//ヘッダー
	private Map<String, String> extraHeaders;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_webview);

		CheckHeader();

		// WebViewインスタンスを取得
		WebView webView = (WebView) findViewById(R.id.webView);

		//ヘッダーに埋め込む
		extraHeaders = new HashMap<String, String>();
		extraHeaders.put("KeyName", kv.toString());

		//WebViewの設定
		webView.getSettings().setUseWideViewPort(true);
		webView.getSettings().setLoadWithOverviewMode(true);
		webView.setVerticalScrollbarOverlay(true);
		webView.getSettings().setBuiltInZoomControls(true);
		webView.getSettings().setDomStorageEnabled(true);
		webView.getSettings().setJavaScriptEnabled(true);
		webView.loadUrl(url);

		// WebViewClientを継承
        webView.setWebViewClient(new WebViewClient() {
			// リンクをタップしたときに標準ブラウザを起動させない。
			@Override
			public boolean shouldOverrideUrlLoading(WebView webView, String url) {
				if (url.startsWith("http:") || url.startsWith("https:")) {
					//インストール状況をチェックするURLのときだけ独自ヘッダーを埋め込む
					//(独自ヘッダーは最初にアクセスしたURLしか有効でないため? 別のページに遷移すると独自ヘッダー情報が失われている)
					if (url.equals(CHECK_URL)) {
				        webView.loadUrl(url, extraHeaders);
					}

					return false;
				}
				// アンドロイドマーケットのときは例外
				else {
					Uri uri = Uri.parse(url);
					Intent intent = new Intent(Intent.ACTION_VIEW, uri);
					startActivity(intent);
					return true;
				}
			}
		});
	}

//ヘッダーをつくる
	private void CheckHeader() {
		//情報をJSON化
        kv = new JSONObject();
        try {
    		for (int i = 0; i < install.length; i++) {
    			kv.put("key" + i, i);
    		}
        } catch (JSONException e2) {
            e2.printStackTrace();
        }

		System.out.println("kv:" + kv.toString());
	}

SurfaceViewで画像を描画する

いよいよ画像を描画します。
前回の記事で生成したBitmapですが、そのまま貼り付けようとすると、端末によって画面の大きさが違い座標がバラバラになるため、同じ画面になりません。
画面の大きさに応じて座標を再計算して、その上で描画しなくてはなりません。

[AnimationSurfaceView.java]

	private void Draw() {
		//背景
		drawBitmap(bm[Const.IMAGE_BG], Const.POS_X_BG, Const.POS_Y_BG);
		//枠
		drawBitmap(bm[Const.IMAGE_WAKU], Const.POS_X_WAKU, Const.POS_Y_WAKU);
		//ブロック
		DrawBlock();
		//バー
		drawBitmap(bm[Const.IMAGE_BAR], bar_pos_x, bar_pos_y);
		//ボール
		int draw_ball_pos_x = ball_pos_x / Const.DRAW_MAGNI;
		int draw_ball_pos_y = ball_pos_y / Const.DRAW_MAGNI;
		drawBitmap(bm[Const.IMAGE_BALL], draw_ball_pos_x, draw_ball_pos_y);
	}

	private void drawBitmap(Bitmap image, int x1, int y1) {
		int draw_x1 = Const.exchangePOSX(x1, screen_width);
		int draw_y1 = Const.exchangePOSY(y1, screen_height);
		
		main_canvas.drawBitmap(image, draw_x1, draw_y1, paint_iamge);
	}

いったん、drawBitmap()というメソッドを経由して座標を計算しています。
この座標というのは、想定するもともとの画像の大きさと照らし合わせて計算されます。
計算メソッドは下記のようになります。
[Const.java]

	public static int IMAGE_WIDTH	= 320;
	public static int IMAGE_HEIGHT	= 480;
	public static int DRAW_MAGNI	= 100;

	public static final int POS_X_BG = 0;
	public static final int POS_Y_BG = 0;
	
	public static final int POS_X_WAKU = 10;
	public static final int POS_Y_WAKU = 20;
	public static final int WAKU_W = 300;
	public static final int WAKU_H = 440;
・
・・・その他の定数宣言は省略
・

	//描画枠のサイズに合わせて実際の座標を求める
	public static int exchangePOSX(int calc_x, int width) {
		float magni = (float)width / IMAGE_WIDTH;
		float draw_x = (float)calc_x * magni;
		return (int) draw_x;
	}
	public static int exchangePOSY(int calc_y, int height) {
		float magni = (float)height / IMAGE_HEIGHT;
		float draw_y = (float)calc_y * magni;
		return (int) draw_y;
	}

この描画のやり方で、どんな端末でも同じ見え方になるはずです。。。

SurfaceViewで使用する画像を読み込む

これは、キャンバス系のView全般で使えるメソッドで、別にSurfaceViewに限ったものではありません。
画面の大きさに左右されず一定の比率でBitmapを生成しています。
注意すべきは生成のタイミングで、Viewを呼び出すタイミングで一回だけ生成するようにしてください。
特にSurfaceViewだとコード内でループしているので、何度も画像生成するようなタイミングでメソッドを呼び出さないように注意してください。

自分の場合は、
public void run() {
すぐあとくらいの行で記述しています。
while(true)の中に入れないように。

[AnimationSurfaceView.java]

//宣言は別の場所で
Bitmap bm[];
・
・
・
	@Override
	public void run() {
・
・
・
		//画像の読み込み
		Resources res = getResources();
	    bm = new Bitmap[Const.IMAGE_ID.length];
	    for (int i = 0; i < bm.length; i++) {
	    	System.out.println("i ->" + i);
	    	bm[i] = Const.exchangeImage(screen_width, screen_height, res, Const.IMAGE_ID[i]);
	    }
・
・
・

定数やメソッドをまとめて記述しているConstクラスを別ソースで保存します。
[Const.java]

	//AndroidにおいてはEnumは使うべきではない
	public static final int IMAGE_BG		= 0;
	public static final int IMAGE_WAKU		= 1;
	public static final int IMAGE_BAR		= 2;
	public static final int IMAGE_BALL		= 3;
	public static final int IMAGE_BLOCK1	= 4;
	public static final int IMAGE_BLOCK2	= 5;
	public static final int IMAGE_BLOCK3	= 6;

	public static final int[] IMAGE_ID = {
		R.drawable.bg,
		R.drawable.waku,
		R.drawable.bar,
		R.drawable.ball,
		R.drawable.block1,
		R.drawable.block2,
		R.drawable.block3,
	};
	
	public static Bitmap exchangeImage(int width, int height, Resources resources, int id) {
		//拡縮の倍率
		float magni_x = (float)width / IMAGE_WIDTH;
		float magni_y = (float)height / IMAGE_HEIGHT;
		
	    //拡縮して生成
	    Matrix matrix = new Matrix();
	    matrix.postScale(magni_x, magni_y);
	    
	    BitmapFactory.Options options = new BitmapFactory.Options();
	    options.inScaled = false;
	    options.inJustDecodeBounds = false;
	    Bitmap _bm = BitmapFactory.decodeResource(resources, id, options);
	    Bitmap bmp = Bitmap.createBitmap(_bm, 0, 0, _bm.getWidth(), _bm.getHeight(), matrix, true);
	    _bm.recycle();
	    _bm = null;
	    return bmp;
	}

このConst.javaの中の
R.drawable.bg

R.drawable.bar
が、画像ファイルです。
IDをint型配列の中にまとめておきます。
こうすることで、画像が減ったり増えたりしても、Constをいじるだけですぐに対応できます。

で、問題は画像の配列の添え字を別に
public static final int IMAGE_BG = 0;
public static final int IMAGE_WAKU = 1;
このように定義している件です。
これはコメントにも書いてある通り、AndroidアプリにおいてはEnumは使うべきでないという判断からこうしています。
なぜEnumを使ってはダメかというと、詳しくはググってください。負荷対策らしいです。
少々面倒ではありますが、Androidアプリ開発において、メモリリークとの戦いは常につきまとう問題です。
こういう所から意識していきましょう。

SurfaceViewを動かす

AndroidアプリにおいてはSurfaceViewは速度が安定しない(特にタブレット端末など、画面が大きいもので画像を動かそうとすると著しくパフォーマンスが落ちます)ので、本格的なゲームを作ろうとしているときなどはあまり使われません。

しかし、ちょっとした動きのあるアプリを作りたいときなどはこれで十分です。

とはいえ、情報があまりなく、独特なつくりなので実装しにくいViewではあります。

ちなみに、前回の記事の続きです。

[AnimationSurfaceView.java]

public class AnimationSurfaceView extends SurfaceView implements Runnable, SurfaceHolder.Callback {
	
	Canvas main_canvas;

	static final long FPS = 30;
	static final long FRAME_TIME = 1000 / FPS;
	SurfaceHolder surfaceHolder;
	Thread thread;
	int screen_width, screen_height;
	
	public AnimationSurfaceView(Context context) {
		super(context);
		surfaceHolder = getHolder();
		surfaceHolder.addCallback(this);
	}

	@Override
	public void run() {
		main_canvas = null;

		long loopCount = 0;
		long waitTime = 0;
		long startTime = System.currentTimeMillis();
		
		while (thread != null) {

			try {
				loopCount++;
				main_canvas = surfaceHolder.lockCanvas();

				//処理系
				Update();
				
				//描画系
				Draw();
 
				surfaceHolder.unlockCanvasAndPost(main_canvas);

				waitTime = (loopCount * FRAME_TIME) - (System.currentTimeMillis() - startTime);

				if (waitTime > 0) {
					Thread.sleep(waitTime);
				}
			} catch (Exception e) {
			}
		}
	}
	

	private void Draw() {
	}

	private void Update() {
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		screen_width = width;
		screen_height = height;
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		thread = new Thread(this);
		thread.start();
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		thread = null;
	}
}

レイアウトの一部にSurfaceViewを適用する

下記の例では、全画面に表示するようにしていますが、
android:id=”@+id/surfaceView”
とid付けを入れれば、画面の一部でもSurfaceViewが適用されます。

レイアウト
[activity_main.xml]

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/surfaceView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
</LinearLayout>

アクティビティ
[MainActivity.java]

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		LinearLayout ll = (LinearLayout)findViewById(R.id.surfaceView);
		AnimationSurfaceView surface_view = new AnimationSurfaceView(this);
		ll.addView(surface_view);
	}
}

SurfaceViewを継承した別クラスを定義し、それをaddViewしています。
SurfaceViewの具体的なソースは、また別の記事で。

Top