カテゴリー ‘ Android

Androidで複数のImageViewを1枚の画像に合成保存する方法


Androidで複数のImageViewを1枚の画像に合成保存する方法

面白いカメラアプリが作りたい!

でも、画像が上手く合成出来ない…と言う事で合成の方法を調べた備忘録

Androidで複数ImageViewを使用した写真合成アプリのような物を想像してください。

その時に、見た目そのままの内容で画像を保存したいというのが、普通です。

じゃあ見たままの内容を保存する為にはどうしたらいいのでしょうか?

まずは手順から

  1. 背景となるBitmapを生成する
  2. 1.で作成したBitmapインスタンスを元にCanvasを生成する
  3. 2.で作成したCanvasに必要なImageViewから取得したBitmapとMatrixを使用してdrawBitmapを行う
  4. ファイルに出力する
このような手順になります。
手順1
ImageView imageView = (ImageView) findViewById(R.id.base_image);
Bitmap newBitmap = Bitmap.createBitmap(imageView.getWidth(), imageView.getHeight(), Bitmap.Config.ARGB_8888);

画面の表示サイズを取得する為、1行目でImageViewのインスタンスを取得これは、画面サイズがわかるViewであればImageViewである必要はないです。

手順2

Canvas offScreen = new Canvas(newBitmap);

手順1のBitmapを元にCavasを作成する、これで画像のベースとなるCanvasが出来上がる。

手順3

ImageView imageView1 = (ImageView) findViewById(R.id.image1);
ImageView imageView2 = (ImageView) findViewById(R.id.image2);
Bitmap bitmap1 = ((BitmapDrawable) imageView1.getDrawable()).getBitmap();
Bitmap bitmap2 = ((BitmapDrawable) imageView2.getDrawable()).getBitmap();
offScreen.drawBitmap(bitmap1, imageView1.getImageMatrix(), null);
offScreen.drawBitmap(bitmap2, imageView2.getImageMatrix(), null);

Canvasに必要な数だけdrawBitmapするその時にImageViewの中のMatrixを使用するように注意

手順4

OutputStream outputStream = null;
try {
	outputStream = getContentResolver().openOutputStream(mUri);
	newBitmap.compress(CompressFormat.JPEG, 100, outputStream);
} catch (FileNotFoundException e) {
} finally {
	if (outputStream != null) {
		try {
			outputStream.close();
		} catch (IOException e) {}
	}
}

ファイルに出力

ここでは、前もって作成済みのUriクラスのインスタンスmUriを使用してファイル出力を行うように記述しています。

FileOutputStreamを使用して普通にファイル出力を行っても問題ありません。

以上で、ImageViewの画像合成の出来上がりです。

Androidのサービスでintentを使うとNullPointerExceptionで落ちるのを回避


Androidのサービスでintentを使うとNullPointerExceptionで落ちるのを回避

AdnroidのサービスをstartServiceで呼び出した場合、

「onStartCommand」第1引数のintentを使用するとNullPointerExceptionが発生する場合があるので注意が必要です。

サービス起動時のソースコードとサービス側のソースを見てる

サービス起動側

// サービス起動
Intent service = new Intent(getApplication(), LightService.class);
service.putExtra(LightService.CALL_INTENT, mode);
startService(service);

サービス側

@Override
public void onStart (Intent intent, int startId)
	int mode = intent.getIntExtra(CALL_INTENT, INTENT_ACTIVITY);
	if (mode == INTENT_WIDGET) {
		onModeSwitch(true);
	} else {
		onModeSwitch(false);
	}
}

これで起動をかけるとサービス側の3行目でNullPointerExceptionが発生して落ちる場合があります

ここで一番の問題なのは「onStart」を使ってしまっている事です。

「onStart」は現在では「This method is deprecated.」となっており

代わりに「onStartCommand」というメソッドを使用しないといけないということらしいです。

それともう一点、「onStartCommand」を使用した場合にも問題があります。

ドキュメントの以下の用に記述されています。

intent The Intent supplied to startService(Intent), as given. This may be null if the service is being restarted after its process has gone away, and it had previously returned anything except START_STICKY_COMPATIBILITY.

ようするに、戻り値によっては引数のintent変数にNULLが入ってくる場合があるようです。

なので、NULLが入ってこないように戻り値を「START_STICKY_COMPATIBILITY」にして、

大丈夫なはずだが、念の為にintentに対しNULLチェックの追加(これは無駄な処理のハズ・・・)

で修正後のサービス側のソースがこちら

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
	int mode = INTENT_ACTIVITY;
	if (intent != null) mode = intent.getIntExtra(CALL_INTENT, INTENT_ACTIVITY);
	if (mode == INTENT_WIDGET) {
		onModeSwitch(true);
	} else {
		onModeSwitch(false);
	}
	return START_STICKY_COMPATIBILITY;
}

このように修正して対応しました。

 

調べた場合、以下のブログを参考にしました。ありがとうございます。

 

AndroidManifest.xmlに記述したのデータ取得方法


AndroidManifest.xmlに記述した<meta-data>の取得方法

 

この方法を使うとアプリケーションで固定して使う値を一元管理出来ます。

strings.xmlで管理する方法もありますがどちらがいいのでしょうか?

とりあえず、AndroidManifest.xmlに以下のように<meta-data>を記述します。

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".TestDroid"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

		<meta-data android:value="test_value" android:name="test_key" />
    </application>

10行目がmeta-dataの記述です。

この値をプログラム側で取得する方法です。

ApplicationInfo appliInfo = null;
try {
	appliInfo = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {}
Log.d("MetaData", appliInfo.metaData.getString("test_key"));

このように記述します。

ちなみに、例はActivityの中での使用方法なので、

Contextのメソッドの「getPackageManager」や「getPackqgeName」は適時インスタンス変数からの呼び出しに変更してください。

Androidでのカメラ連動するアプリでのカメラの呼び出し方とデータ取得方法


Androidでカメラと連動するアプリの作り方

2012/8/14 コメントを頂き、記事を追記修正し、サンプルプログラムを作成しました。

画像を編集するようなアプリを作成する場合、カメラをIntent経由で呼び出してとった写真のデータを戻してもらうことがあると思います。

その時にどのようにしたら、カメラを起動できて、どのようにしたらそのデータが取れるのか調べた結果です。

Intentでのカメラの呼び出し方

google謹製の「com.android.camera」カメラアプリを調べると2つそれらしいintent-filterが設定されていました。

    1. android.media.action.IMAGE_CAPTURE
    2. android.media.action.STILL_IMAGE_CAMERA

の2つです。

どちらを使えばいいか調べてみると

正解は、「android.media.action.IMAGE_CAPTURE」でした。

もう一つの方は連携するのでは無く、静止画モードでカメラアプリを起動するというintent-filterでした。

ですので、まずカメラアプリに写真をとってそのデータを返却して欲しい場合は、以下のようにカメラアプリを起動する必要があります。

 

Intentでカメラを呼び出した場合のデータの取得方法

これもまた、google謹製の「com.android.camera」カメラアプリを調べてみました。

2通りあることが分かりました。

  • 呼び出し時に保存して欲しいパスを指定する方法
  • 生データをそのまま受け取る方法
カメラアプリでは、

こんな感じにファイルパスを取得していました。
で、さらに

こんな感じでファイルを作成したりデータをintentにセットしていました。
なので取得する側では、

こういう風に処理して取得できます。
ですが…、Bitmapの画質が悪過ぎでこれは使い物になりません。
なので、画像ファイルとして保存してもらって、データを取得するように変更します。
まずは、呼び出し処理、

Uriオブジェクトを生成して、IntentにputExtraでキーを「MediaStore.EXTRA_OUTPUT」でセットします。

Uriオブジェクトの生成方法は、こんな感じです。

SDカードに書き出しをしているのでAndroidManifest.xmlに以下のパーミッションを追加します。

で、データの取得方法は、

このように指定したUriにファイルが作成されるので取得すると操作出来るようになります。
で、色々調べていたら詳しく書かれているサイトがありました。
こちらを参考にする方がいいかもです。

画面の回転でUriが初期化される場合の対応方法

今回は、UriをActivityのインスタンス変数として保持しました。

その為に、Activityが再生成されてしまうとUriが初期化されてしまいます。

画面の縦横の回転の処理が動作するとAndroidは、Activityを破棄して再生成します。そうなると見事インスタンス変数はNULLになります。

これを防ぐ為の方法は、2つ程あります。

  1. 回転時の処理を独自に実装にしてActivityの再生成を防ぐ。
  2. Activityの破棄、再生成時に状態を保持するように処理を追記する。

今回サンプルプログラムでは2.の方法で実装しました。

まずは、

1.回転時の処理を独自に実装にしてActivityの再生成を防ぐ

こちらの実装は簡単で、まずは「AndroidManifest.xml」に「android:configChanges=”orientation|screenSize”」の記述を追記します。

そうすると、画面の回転時にActivtyの再生成が行われずに「onConfigurationChanged」が実行されます。

これで、画面の回転でActivityが初期化される事はありませんが、横になっても、レイアウトが横になりません。

2.Activityの破棄、再生成時に状態を保持するように処理を追記する

今回サンプルではこちら、

  • protected void onSaveInstanceState(Bundle outState)
  • protected void onRestoreInstanceState(Bundle savedInstanceState)

の2つのメソッドをオーバーライドをして、

「onSaveInstanceState」でデータの保存を

「onRestoreInstanceState」でデータの復元を行います。

これで、インスタンス変数の情報を保持します。

今回の場合は

「onSaveIncetanceState」で、

このように、Bundleにインスタンス変数のUriを保存して

「onRestoreInstanceState」でこのように

データの復元を行います。

動作サンプル:https://github.com/nasneg/SampleCameraIntent

参考になるサイト

注)表示される画像の角度が撮影時と違うと思いますが、Exif情報から修正する為には、こちらの記事を参考にして下さい。

android.git.kernel.orgが休業状態中でもandroidのソースを見る方法


 

こちらの記事の情報は2012年1月21日時点では、もう古くなってしまっています。

Androidのソースを参照する為の記事を書いたのでこちらを参照下さい。

 

2011年9月6日時点、Androidのソースコードが置いてあったgitが「このような事件」により参照することが出来なくなっています。

でも、やっぱりアプリ作っていると本家のソースコードを見て参考にしたいなと思う部分というのが、いくつか出てくると思います。

そのような時には、「Googleソースコード検索」を使うと便利です。

 

対象のgitのurlが分かっているなら対象のurlで検索します。検索結果が表示されると思うので対象箇所をクリックする

そうするとGoogleさんが持っているキャッシュが表示されるので参照する事が可能となります。

 

例えば、以下のようなgitURLで検索してください。

「git://android.git.kernel.org/platform/packages/apps/Settings.git」

Googleソースコード検索 - git://android.git.kernel.org/platform/packages/apps/Settings.git

このような検索結果の一覧が表示されるので、一覧の中の見たいものをクリックします。

(見たいものが直接みつからない場合、同じgitリポジトリなら大丈夫です)

 

そうすると、以下のようなページなるので対象ソースを表示して見ることが出来るようになります!

Googleソースコード検索 - git://android.git.kernel.org/platform/packages/apps/Settings.git

 

 

これで、無事ソースコードが見る事が出来るようになりました!

return top