カテゴリー ‘ スマートフォン

AndroidでUriからファイルパスを取得する方法


AndroidでUriでファイルの情報をやりとりするのですが、

実際のFileパスを使用したい場合あるとおもいます。

そういう時に、どうしたらいいかを調べた内容です。

普通にUriからgetPathメソッドでPathを取得すると、

「/external/images/media/401」

のような形でパスの内容が取得されます。

これを実際のファイルパスである、

「/mnt/sdcard/Test_20111101_145318.jpg」

のような形で取得する為には、「ContentResolver」を使用する必要があります。

Uriからファイルパスを取得する実際の処理は以下のようになります。

    /**
     * UriからPathへの変換処理
     * @param uri
     * @return String
     */
    public static String getPath(Context context, Uri uri) {
    	ContentResolver contentResolver = context.getContentResolver();
    	String[] columns = { MediaStore.Images.Media.DATA };
    	Cursor cursor = contentResolver.query(uri, columns, null, null, null);
    	cursor.moveToFirst();
    	String path = cursor.getString(0);
    	cursor.close();
    	return path;
    }

これで、ファイルパスが取得できます。

これで、実際のファイルパスを使用して、ファイルの削除、コピー、移動などができます。

もしかするとこんなことしなくてもUriを使用するだけで、本当は出来るような気はするのですが、

やり方が分からなかったので、実際のファイルパスを取得してしまいました。

 

うまいやり方が他にあったら教えて頂けたらとおもいます。

AndroidでActivityのスタックを削除する方法


Androidで複数Activityからなるアプリの場合、

「A→B→C→A」

のような順番でActivityを順番に呼び出す事があると思います。

この時に、

「A→B→C→A」の「C→A」に移動する時、

前に呼ばれているA,BのActivityのスタックを削除したいようなアプリもあると思います。

なんでかと言うと

「A→B→C→A」と呼ばれたアプリで戻るボタンで戻ると

  1. C
  2. B
  3. A
のように順番に戻ります、これが1週だけならまだいいですが
「A→B→C→A→B→C→A」のような場合
  1. C
  2. B
  3. A
  4. C
  5. B
  6. A
と、戻るボタンで戻る場合かなり面倒な感じになります。
なので「A→B→C→A→B→C→A」の時の最後の「C→A」の時に、
今までのActivityのスタックを全て破棄して新しく「A」が起動してくれた方がありがたい
とおもいませんか?
なので、このようにしたい場合は、以下のようにActivityの呼び出しを変更すると実装できます。
public void onTop(View view) {
	Intent intent = new Intent(getApplication(), BeardMaker.class);
	intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
	startActivity(intent);
}
これで、今までのActivityのスタックが破棄されるので、
戻るボタンを押下した時に、アプリケーションが終了するようになります。

Androidで画像を配列リソース用xmlを使用して管理する方法


まず、「res/values/arrays.xml」にリソースを設定します。

<?xml version="1.0" encoding="utf-8"?>
<resources>
	<string-array name="test_array_drawable">
		<item>@drawable/test1</item>
		<item>@drawable/test2</item>
		<item>@drawable/test3</item>
	</string-array>
</resources>

でjavaの方では以下のようにしてDrawableのインスタンスを取得し使用します。

TypedArray images = context.getResources().obtainTypedArray(R.array.test_array_drawable);
Drawable drawable = images.getDrawable(0);

これで、配列は外出しに出来るのでなるべく外に出したほうが楽かもしれません。

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;
}

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

 

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

 

return top