アンドロイド開発覚え書き


あれ、どうやるんだっけな〜って時に使えるページのつもりです。
なんとなく、ブログは嫌いなので、すごーく昔のホームページ風味でいきます。
開発環境の構築とか、hello worldとかは、どこにでも転がってる思うので当面書かないです。面倒だし。


タイトルバー
カメラ
暗黙的インテント
その他いろいろ



■タイトルバー

タイトルバーを消す
タイトルバーの背景色をグラデーションにする
タイトルバーをカスタマイズする


●タイトルバーを消す
以下のコードを、アクティビティのonCreate内に記述します。
	requestWindowFeature(Window.FEATURE_NO_TITLE);
●タイトルバーの背景色をグラデーションにする
res/valuesの下に、styles.xmlというファイルを作成して以下のように記述します。
(※ちなみに単色塗でよければ、@drawable/titlebar_back_gradとしているところを、#始まりのARGB値で記述するだけです)
	<?xml version="1.0" encoding="utf-8"?>
	<resources>
		<style name="window">
			<item name="android:windowTitleBackgroundStyle">@style/WindowTitleBackground</item>
		</style>

		<style name="WindowTitleBackground">
			<item name="android:background">@drawable/titlebar_back_grad</item>
		</style>
	</resources>
res/drawable-xxxx下に、titlebar_back_grad.xmlというファイルを作成して以下のように記述します。
(※drawable-xxxxの、xxxx部分は、hdpi、ldpi、mdpiのどれでも良いです)
	<?xml version="1.0" encoding="utf-8"?>
	<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
		<gradient
			android:startColor="#ffffffff"
			android:endColor="#ff7f7f7f"
			android:angle="270" />
	</shape>
AndroidManifest.xmlで、以下の指定をしておきます。
	<activity android:name=".HogeAct" android:theme="@style/window">
●タイトルバーをカスタマイズする
背景色や、タイトル文字だけでなく、絵やプログレスバーなどを表示したい場合もあります。
res/layout下に、titlebar.xmlを作成して、以下のように普通にwidgetを配置します。
	<?xml version="1.0" encoding="utf-8"?>
	<LinearLayout
	  xmlns:android="http://schemas.android.com/apk/res/android"
	  android:id="@+id/TitleBar_LinearLayout"
	  android:layout_width="fill_parent"
	  android:layout_height="wrap_content">
		<ImageView
		    android:id="@+id/IconView"
		    android:layout_width="wrap_content"
		    android:layout_height="wrap_content"
		    android:src="@drawable/icon">
		</ImageView>
		<TextView
			android:id="@+id/TitleBar"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="hogehogeアプリ"
			android:textColor="#ff000000">
		</TextView>
	</LinearLayout>
以下のコードを、アクティビティのonCreate内に記述します。
	requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
	getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.titlebar);


■カメラ

オートフォーカスに対応する
縦向きの撮影に対応する


●オートフォーカスに対応する
AndroidManifest.xmlで、以下の指定をしておきます。
	<uses-permission android:name="android.permission.CAMERA" />
	<uses-feature android:name="android.hardware.camera" />
	<uses-feature android:name="android.hardware.camera.autofocus" />
次に、カメラ撮影用のアクティビティ、またはViewに、以下のコードを実装します。
	// シャッターが押されたときに呼ばれるメソッド
	// (※onTouchイベントや、ボタン押下イベントから呼び出す)
	public void shutter(){
		// オートフォーカス開始
		camera.autoFocus(this);
	}

	// cameraから呼び出されるメソッドなので、当該クラスで、
	// implements AutoFocusCallback が記述されている必要がある。
	public void onAutoFocus(boolean flag, Camera camera) {
		// 画像を取得
		camera.takePicture(null, null, this);

		// オートフォーカス停止
	 	camera.autoFocus(null);
	}
※ただ、上記コードで、Nexus S(2.0)では、camera.autoFocus(null) があるとフォーカスが決まらなくなりました。
仕方がないので、Nexus Sでは、camera.autoFocus(null) を呼び出さない実装にしています。

●縦向きの撮影に対応する
andoroidのカメラは基本横向きでの撮影が前提みたいです。
スマートな方法がほかにあれば、教えてほしいですが、自分は以下のようにしました。

1. カメラ用のアクティビティは横向き固定にする。
AndroidManifest.xmlで、以下のように指定しておきます。
こうすれば、端末をグルグル回しても勝手に画面作り直しになりません。
        <activity android:name=".CameraViewAct"
                  android:screenOrientation="landscape">
                                             ^^^^^^^^^^
2. 端末回転時のイベントを補足する。
端末が縦になって撮影された場合は、後で画像を回転させる必要があるので、自分で回転時のイベントを補足する必要があります。
以下のようなクラスを、カメラ用のアクティビティクラス内に作ります。
	private class MyOrientationEventListener extends OrientationEventListener{
		private int orientation = OrientationEventListener.ORIENTATION_UNKNOWN;

		public MyOrientationEventListener(Context context, int rate) {
			super(context, rate);
		}

		@Override
		public void onOrientationChanged(int orientation) {
			int new_orientation = 0;
			if ( OrientationEventListener.ORIENTATION_UNKNOWN == orientation ){
				return;
			}
			if ( orientation >= 315 || ( 0 <= orientation && 45 >= orientation ) ){
				new_orientation = Configuration.ORIENTATION_PORTRAIT;
			}else{
				new_orientation = Configuration.ORIENTATION_LANDSCAPE;
			}

			if ( this.orientation != new_orientation ){
				this.orientation = new_orientation;
				rotate( this.orientation );
			}
		}
	}
カメラ撮影用のアクティビティのonCreate内で、MyOrientationEventListenerを作成します。
oelは、クラスのメンバ変数として定義して下さい。(他のメソッド内でも参照するので)
	oel = new MyOrientationEventListener(this, SensorManager.SENSOR_DELAY_UI);
	oel.enable();
onPause内で、センサーを一旦とめます。
	oel.disable();
onRestart内で、センサーを再開します。
	oel.enable();
で、MyOrientationEventListener.onOrientationChangedから、端末の向きが変わった時に呼びしているrotateというメソッドをカメラ撮影用のアクティビティに実装します。
ここで、現在の向きをアクティビティ内の変数に保持しておきます。(撮影後に参照するため)
	public void onOrientationChanged(int orientation) {
		this.orientation = orientation;
	}
ここまでで、内部的に端末の向きをリアルタイムで保持する仕組みができました。

3. 撮影後に、端末が縦向きの場合は画像を回転させる。
	public void onPictureTaken(byte[] arg0, Camera arg1) {
	if ( Configuration.ORIENTATION_LANDSCAPE != this.orientation ){ // 画面が縦向きの場合
		// 撮影画像のデータからbitmapを作成する
		BitmapFactory.Options opts = new BitmapFactory.Options();
		opts.inPreferredConfig = Bitmap.Config.RGB_565;
		bitmap = BitmapFactory.decodeByteArray(w, 0, w.length, opts);

		// 画像を回転させる
		Matrix matrix = new Matrix();
		matrix.postRotate(90.0f);
		bitmap_rotate = Bitmap.createBitmap(
					bitmap, 0, 0,
					bitmap.getWidth(),
					bitmap.getHeight(),
					matrix, true);
		bitmap.recycle();
	}


■暗黙的インテント

送れる相手を探す
相手におくる


●送れる相手を探す
たとえば、画像ファイルを送信できるアプリケーション(Gmailなど)を列挙するには以下のように記述します。
	// 送るデータのuriをファイルパスから作成する
	Uri uri = Uri.fromFile(path);
	
	// 相手のインテントの条件を設定する
	Intent intent = new Intent();
	// 送信できるアプリケーション
	intent.setAction(Intent.ACTION_SEND);
	// 送るデータはjpeg画像
	intent.setType("image/jpeg");
	// 送るデータのuriを指定する
	intent.putExtra(Intent.EXTRA_STREAM, uri);

	// 条件に合致するインテントのリストを得る
	PackageManager pm = getPackageManager();
	List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
これで、listの中には、対象となるアプリケーションの情報リストが入っています。

Intent.ACTION_SENDを、Intent.ACTION_VIEWにすれば、データを表示できるアプリケーションを列挙できます。

setTypeで、"image/jpeg"を、"application/octet-stream"とすれば、画像だけでなく、バイナリデータを対象にできます。

●相手におくる
前項目のList<ResolveInfo> listが、送れる対象のアプリケーションリストです。
この中の任意の相手にデータを送ればいいわけです。任意の相手については、通常メニューでユーザに選ばせますが、ここではユーザが任意の相手を選んだものとして後続の処理を記述します。
List<ResolveInfo> listの、select番目のアプリケーションが選ばれたものとします。
	ResolveInfo rv = list.get(select);
	Intent intent = new Intent("android.intent.action.MAIN");
	intent.setClassName(rv.activityInfo.packageName, rv.activityInfo.name);
	intent.setAction(Intent.ACTION_SEND);
	intent.setType("image/jpeg");
	intent.putExtra(Intent.EXTRA_STREAM, uri);
	startActivity(intent);
※前段で、Intent.ACTION_VIEWにした場合は、ここでもそうします。
※前段で、"image/jpeg"を、"application/octet-stream"とした場は、ここでもそうします。


■その他いろいろ

OutOfMemory!!
1.6対応アプリから、2.0以降のメソッドを呼び出す
マーケットの更新時


●OutOfMemory!!
画像を扱うアプリで、この例外を何度みたことか・・・。
一番、効果があるのは、以下のコードです。
	bitmap.recycle();
使用済みで、もう参照されないBitmapオブジェクトについては、必ず上記コードを呼び出して下さい。
上記コードを呼び出さずに、bitmap = null とかしても、オブジェクト自体のメモリ領域はガベコレの対象となりますが、bitmapデータそのものが使用しているメモリ領域は解放されません。

●1.6対応アプリから、2.0以降のメソッドを呼び出すs
特にカメラアプリでは、1.6と2.0以降で使えるメソッドに大きな差があります。
アプリは、1.6対応で作成したものの、動作している端末が2.0以降の場合は、2.0用のメソッドを呼び出したい時があるかもしれません。
以下の例は、動作端末が2.0以降の場合に、setColorEffectを呼び出すようにしています。
	// カメラのパラメータを取得する
	Camera.Parameters parameters=camera.getParameters();

	// android2.0以降の場合は、セピアのエフェクトを実行する
	if ( Build.VERSION_CODES.DONUT < Build.VERSION.SDK_INT ){
		Method method = Camera.Parameters.class.getMethod("setColorEffect", new Class[] { String.class });
		method.invoke(parameters, "sepia");
	}
●マーケットの更新時
バージョンを変更しないと、マーケットに新しいモジュールをアップロードできません。
ちなみに変更は、android:versionCode部分だけで大丈夫です。ここには整数値を入れます。
	<manifest xmlns:android="http://schemas.android.com/apk/res/android"
		android:versionCode="3"
		android:versionName="1.0.3" package="com.hogehoge">
inserted by FC2 system