カメラ機能の実装
カメラ機能
を実装する方法は、以下の2通り。
暗黙的インテント
によるOS標準の「カメラ」アプリ
の利用android.hardware.camera2
APIによるカメラ機能
の作成
暗黙的インテントを利用したカメラ機能の実装
参考: 研修11日目
暗黙的インテント
を用いてカメラ機能
を実装する手順は、以下の通り。
マニフェストファイル
に端末のストレージ
を利用するためのパーミッション
を付与- 撮影した
画像ファイル名
が一意となるよう、SimpleDateFormat
を用いて日時フォーマッタ
を作成ストレージ
に格納する画像ファイル名
・ファイル形式
を指定ContentResolver
を用いてデータの格納先URI
を作成暗黙的インテント
を用いてOS標準の「カメラ」アプリ
を起動遷移元アクティビティ
に戻った際に実行される処理を記述
マニフェストファイルへのパーミッションの記述
参考: 研修12日目
アプリケーション
が端末のストレージ
を利用できるよう、マニフェストファイル
(=AndroidManifest.xml
)に<uses-permission>
タグを追記する。
なお、ユーザ
に対してパーミッションダイアログ
の表示が必要なのは、
アプリケーション
に付与されたパーミッション
を用いて外部(=インターネット)との通信が行われる場合に限定されるため、端末内部でデータのやり取りを行う場合はパーミッションダイアログ
の表示は不要である。
サンプルコード
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
/>
...
</manifest>
日時フォーマッタの作成
ストレージ
にファイル
を格納する場合、ファイル名が同名となり自動で上書きされないよう、一意となるファイル名にする必要がある。
ファイル名を一意とするためには、タイムスタンプ
を利用する。
タイムスタンプ
の利用にあたって、日時フォーマッタ
であるSimpleDateFormat
オブジェクト、現在時刻
を保持するDate
オブジェクトを利用する。
ここで、Date
オブジェクトをタイムスタンプ
として利用する場合、
タイムスタンプ
はString
型であるため、
DateFormat
クラスのformat()
メソッドを用いてDate
オブジェクトをString
型に変換する必要がある。
SimpleDateFormat
アルファベットを用いたユーザ定義
の日時フォーマッタ
を生成するクラス。
Date
ミリ秒
単位で特定の「瞬間」を表すクラス。
定義
// 日時フォーマットを指定したSimpleDateFormatオブジェクトの生成
SimpleDateFormat(pattern: String)
// パラメータ
// pattern: 日時の形式
// Date型 -> String型 への変換
// <- SimpleDateFormatクラスはDateFormatクラスを継承しているため、
// SimpleDateFormatクラスからも利用可能
DateFormat.format(date: Date): String
// パラメータ
// date: 日時を表すDateオブジェクト
日時形式の主な表現方法
文字 | 内容 | 例 |
---|---|---|
yyyy |
年 | 2021 |
MM |
月 | 06 |
dd |
日 | 21 |
EEE |
曜日 | Mon |
HH |
時 | 10 |
mm |
分 | 56 |
ss |
秒 | 48 |
SSS |
ミリ秒 | 978 |
サンプルコード
// 日時形式を指定したSimpleDateFormatオブジェクト
val dateFormat = SimpleDateFormat("yyyyMMddHHmmss")
// 現在の時刻を保持するDateオブジェクト
val now = Date()
// Date型 -> String型 への変換
val nowStr = dateFormat.format(now)
// タイムスタンプを利用したファイル名の指定
val fileName = "CameraIntentPhoto_${nowStr}.jpg"
ContentValueオブジェクトの生成・定義
ContentValue
オブジェクトを生成し、やり取りするデータのファイル名
と、データの種類
(=MIMEタイプ
)を定義する。
ContentValue
ContentResolver
が生成するURI
に含まれる、コンテンツプロバイダ
が取り扱うデータ情報
をマップ
構造で保持するクラス。
ContentResolver
コンテンツプロバイダ
に渡す、データの格納先URI
(=Uri
オブジェクト)とデータ情報
(=ContentValue
オブジェクト)が含まれるURI
を生成するクラス。
コンテンツプロバイダ(ContentProvider)
参考: コンテンツプロバイダ
端末のストレージ
や複数の外部アプリケーション
と直接データ
のやり取りを行う、アプリケーション
の構成要素。
コンテンツプロバイダ
は、ContentResolver
オブジェクトによって作成されたURI
を基に、指定されたストレージ
に対して指定されたデータ
をやり取りする。
端末のストレージ
へのアクセス手順を図式化すると、以下のようになる。
定義
ContentValues.put(
key: String!,
value: <T>!
): Unit
// パラメータ
// key: データの種類を表すキー
// value: 値
データの種類を表すContentValuesのキー
キー名 | 内容 |
---|---|
MediaStore.Images.Media.TITLE |
画像ファイル名 |
MediaStore.Images.Media.MIME_TYPE |
ファイルの種類(=MIMEタイプ ) |
サンプルコード
// タイムスタンプを利用したファイル名の指定
val fileName = "CameraIntentPhoto_${nowStr}.jpg"
// ContentValuesオブジェクト
val values = ContentValues()
// ContentValuesオブジェクトに格納するファイル名
values.put(MediaStore.Images.Media.TITLE, fileName)
// ContentValuesオブジェクトに格納するMIMEタイプ(=ファイルの種類)
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
Uriオブジェクトの生成
ContentResolver
クラスのinsert()
メソッドを用いて、
「データの格納先URI」(=Uri
オブジェクト)と、
格納する「データ情報」(=ContentValues
オブジェクト)を保持するUri
オブジェクトを生成する。
なお、ContentResolver
がコンテンツプロバイダ
に対して渡すURI
はリレーショナルデータベース
のデータ構造をとっており、
URI
に含まれるデータの格納先URI
毎にテーブル
が存在し、
テーブル
毎に取り扱うデータ情報
が含まれる。
上記サンプルコードの場合のテーブル
のデータ構造は、以下の通り。
キー | 値 |
---|---|
MediaStore.Images.Media.TITLE |
${filename} |
MediaStore.Images.Media.MIME_TYPE |
"image/jpeg" |
定義
ContentResolver.insert(
url: Uri,
values: ContentValues?
): Uri?
// パラメータ
// url: 「データの格納先」を表すUriオブジェクト
// values: 「データ情報」を表すContentValuesオブジェクト
サンプルコード
// ContentValuesオブジェクト(=データ情報)
val values = ContentValues()
// ContentValuesオブジェクトに格納するファイル名
values.put(MediaStore.Images.Media.TITLE, fileName)
// ContentValuesオブジェクトに格納するMIMEタイプ(=ファイルの種類)
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
// データの格納先Uriとデータ情報(=ContentValuesオブジェクト)を保持するURI
_imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
暗黙的インテントの定義
参考: 研修11日目
インテント
が行うアクション
がアプリケーション
そのものを表す場合、URI
の指定は不要となる。
また、「カメラ」アプリ
を利用し、撮影した画像データ
をストレージ
に保存する場合、
インテント
のExtraデータ
に、ContentResolver
を用いて作成したURI
を追加する。
ただし、その場合のExtraデータ
のキー(=name
)は、値が「データの出力先」であることを表すMediaStore.EXTRA_OUTPUT
とする。
定義
// 暗黙的インテントを利用したIntentオブジェクトの生成
Intent(action: String!)
// パラメータ
// action: インテントが実行するアクション
// データの出力先URIを指定するExtraデータの追加
Intent.putExtra(
name: String!,
value: Parcelable?
): Intent
// name: 格納するデータの名称
// value: 格納するデータ
サンプルコード
// 「カメラ」アプリを起動するインテント
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
// 画像の出力先をURIで指定
intent.putExtra(MediaStore.EXTRA_OUTPUT, _imageUri)
遷移先での処理後に遷移元へ戻る画面遷移の実装
参考: 研修3日目
遷移先アクティビティ
での処理終了時に、自動的に遷移元アクティビティ
に戻る場合、
ComponentActivity
クラスのstartActivityForResult()
メソッドを用いてインテント
を開始する。
定義
ComponentActivity.startActivityForResult(
intent Intent!,
requestCode: Int
): Unit
// パラメータ
// intent: 遷移先アクティビティを表すIntentオブジェクト
// requestCode: onActivityResult()メソッド(後述)と
// 一対一で対応させるリクエストコード
サンプルコード
// 「カメラ」アプリを起動するインテント
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
// 遷移先での処理後に遷移元へ戻る画面遷移の実行
startActivityForResult(intent, 200)
画面遷移後に遷移元で呼び出される処理の記述
ComponentActivity
クラスのstartActivityForResult()
メソッドを用いてインテント
を開始した場合、遷移先アクティビティ
での処理終了後、
遷移元アクティビティ
でComponentActivity
クラスのonActivityResult()
メソッドが呼び出される。
また、遷移元アクティビティ
で撮影した画像データ
を表示する場合は、
あらかじめImageView
を配置しておき、ImageView
に対して画像データが保存されている格納先ストレージ
のURI
を指定する。
定義
// startActivityForResult()メソッドの終了時に呼び出される処理
@CallSuper
ComponentActivity.onActivityResult(
requestCode: Int,
resultCode: Int,
@Nullable data: Intent?
): Unit
// パラメータ
// requestCode: startActivityForResult()メソッドと
// 一対一で対応させるリクエストコード
// resultCode: 処理結果を表すActivityクラス定数
// URIを指定してImageViewに画像を表示
ImageView.setImageURI(uri: Uri?): Unit
// uri: 画像データのURI
処理結果を表すActivityクラス定数
定数名 | 内容 |
---|---|
RESULT_OK |
正常終了 |
RESULT_CANCELED |
キャンセル |
サンプルコード
// データの格納先URIとデータ情報(=ContentValuesオブジェクト)を保持するURI
_imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
// startActivityForResult()による遷移先アクティビティでの処理終了後、
// 遷移元アクティビティで呼び出される処理(=コールバック処理)
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// リクエストコードが一致し、かつ正常に処理が終了していた場合の処理
if(requestCode == 200 && resultCode == AppCompatActivity.RESULT_OK) {
// 「カメラ」アプリによって変換されたBitmapオブジェクト(=画像)
// <- ストレージを利用しない場合はサムネイル画像しか取得できないため、
// 解像度の低い画像となる
// val bitmap = data?.getParcelableExtra<Bitmap>("data")
// 画像を表示するImageView
val ivCamera = findViewById<ImageView>(R.id.ivCamera)
// Bitmapデータを指定してImageViewに画像を反映
// ivCamera.setImageBitmap(bitmap)
// URIを指定してImageViewに画像を反映
ivCamera.setImageURI(_imageUri)
}
}