Senの競技プログラミング備忘録

こけた問題を自分用の解説で載せる。けんちょんさんのブログを目指したい。質的にも量的にも。こけた問題だけに限定するけど

Android開発 for Kotlin 自分向けのメモ帳追加パック 動的な部分-暗黙的Intent

自分向けのメモ。たぶん新入生教育にも開発仲間にも役に立ちそう。(あくまで自分向けのsummaryだが)

動的な部分
sen-comp.hatenablog.com

暗黙的Intentとそれに係る操作

大体どころか全部ここにある。
developer.android.com

第2引数のURIが不要なものもある。
intentでのデータの受け渡しだが、非premitive型はparcelableインターフェースを実装して、getParcelableExtra<>()などを使用して受け取る。

写真撮影+保存

写真をただ撮影したいときは、第2引数のURIが不要であり、次のようなもので書ける。

//以下の定数を使う
MediaStore.ACTION_IMAGE_CAPTURE

val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val requestId : Int = 334
//startActivity()すると写真撮影して得られた写真を手に入れることができない。
startActivityForResult(intent, requestId)

これは渡したintentを介在して、写真データがputExtraされている。なので、onActivityResult()をオーバーロードしてそこでリクエストコードで場合分けして処理が必要である。
具体的には、写真のデータはBitmapというデータとラベル"data"でintentに保存されている。これは、primitive型ではないので、取るにはgetParcelableExtra("data")とやる必要がある。(Bitmap自体はParcelableインターフェースを継承している。)

val bitmap = intent.getParcelableExtra<Bitmap>("data")
//このようにImageViewにセットできる。
findViewById<ImageView>(R.id.img).setImageBitmap(bitmap)

しかしこれではとても小さい画像になっていて実用性に欠ける。
より大きな画像にするには、一旦外部ストレージに保存して、それを利用する必要がある。それを達成するには次のステップを踏まなければならない。

1. 外部ストレージのアクセスの許可
2. ファイルの保存場所、名前をURIで定義
3. URIでカメラの写真の保存場所を指定してIntentでカメラを起動

外部ストレージのアクセス許可

AndroidManifest.xmlに次のことを記述。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

また、外部ストレージを利用する前に、条件分岐で当然チェックしないといけない。

if(ActivityCompat.checkSelfPermission(this@MainActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
    != PackageManager.PERMISSION_GRANTED){
    //ここに許可されてない場合を記述
}
ファイルの保存場所のURIの定義

AndroidのImageViewでは、URIを使って、ストレージの画像を表示することができる(setImageURI())。それ以外でも、intentのデータに適宜な定数をラベルにすることで、URIで指定した場所に勝手に保存してくれる(後述)機能もあるため、URIは必須となる。
URI自体は、次の手順で生成される。

1. ContentValuesオブジェクトの設定に必要な画像ファイル名を生成。
2. Uriオブジェクト生成に必要なContentValuesオブジェクトを生成する。
3. ContentResolverオブジェクトからUriオブジェクトを生成する。

ContentValuesオブジェクトの設定をまず行う。
まず、写真のファイル名を用意する。ファイル名の一意性の保証のしやすさの面から考えると、現在時刻を利用したファイル名がよく使われる。詳細的なコード例は以下の通り。

//日にちのフォーマットとしてはこれのほかにもいくつかある
val dateFormat = SimpleDateFormat("yyyyMMddHHmmss")
//Date()で現在時刻を求められる。
val now = Date()
//このように文字列へ変換できる。
val nowStr : String = dateFormat.format(now)
//最後にこのようなファイル名にすれば唯一性が(ほぼ確実に)担保される
val fileName = "UseCameraActivityPhoto_${nowStr}.jpg"

次に、このようにして作成したファイル名fileNameを使って、URI作成に必要なContentValuesオブジェクトを生成する。ContentValues自体は、データベース系のクラスcontentResolverに値を追加するためのクラスで、Bundleみたいに値を一元的に管理できる。これからそのContentValuesに必要なデータを追加していく。ソースコード参照。

//このように作ると、空のContentValuesが作成される。
val values = ContentValues()
//以下のように、ファイル名と拡張子は指定しなければならない。
values.put(MediaStore.Images.Media.TITLE, fileName)
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
ContentResolverオブジェクトからUriオブジェクトを生成

実際にURIオブジェクトを生成するのはContentResolverオブジェクトであり、これはActivityクラスの親クラスであるContextWrapperが保持しているcontextResolverとして使用される。(コード参照)これのメンバメソッドのinsert()を使用して、新たなデータの格納先を確保して、それを表すUriオブジェクトを生成してくれる。コード参照。

//第1引数の定数文字列を渡すことによって、Uriを生成される。
val _imageUri : Uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)

あとは、これをintentのExtraデータとして渡し、Intent自体にMediaStore.ACTION_IMAGE_CAPTUREを渡してActivityを開始すればよい。。

val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra(MediaStore.EXTRA_OUTPUT, _imageUri)
startActivityForResult(intent, 200)

画像やメディアファイルの外部ストレージへの書き込みなどは以下のサイトが詳しい。
写真を撮影する  |  Android デベロッパー  |  Android Developers

外部ストレージ内のメディア ファイルを操作する  |  Android デベロッパー  |  Android Developers

参考文献

野崎英一. やさしいKotlin入門. カットシステム. 2018/5/10

齋藤新三(著). 山本祥寛(監修). 基礎&応用力をしっかり育成!Androidアプリ開発の教科書 Kotlin対応 なんちゃって開発者にならないための実践ハンズオン Kindle版. 翔泳社. 2019/7/10