Android Studio カメラアプリ作成その16(画像ファイルに保存2)
こんにちは かなで です。
参考サイト
https://developer.android.com/training/camerax/take-photo?hl=ja
によると、画像を保存するための記述はこれで終わりみたいです。
fun onClick() { val outputFileOptions = ImageCapture.OutputFileOptions.Builder(File(...)).build() imageCapture.takePicture(outputFileOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback { override fun onError(error: ImageCaptureException) { // insert your code here. } override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { // insert your code here. } }) }
いつも通りどこに書くかはこちら↓
たぶん間違ってるんですが、一旦ここでお願いします。
そして、入力する文言ですが、
この部分だけを入力します。
というのも、「fun onClick() { }」で、何かのオブジェクトをクリックしたときに、赤枠を処理する
という動作のはずですが、今は、とりあえず保存したいだけですので、「クリックしたとき(タップしたとき」の処理じゃなくて、事前処理の流れの中で保存までさせてみたためです。
今後、場所を変えることになると思いますが、まだ正解がわかってないのでお許しください。
コピペすると、このようになりました。↑
インポートできたのは「ImageCaptureException」と「File」
インポート後の表示↑。一見Fileは解決してるように見えますが、問題ビューを見ると4つのエラーが。
Overload resolution ambiguity: public constructor File(uri: URI!) defined in java.io.File public constructor File(pathname: String!) defined in java.io.File Expecting ')' Expecting an expression Unexpected tokens (use ';' to separate expressions on the same line)
まずはこの解決からしていきたいと思います。
調べてみると「File(...)」に入るのは「保存するファイル名をフルパス」が正解のようです。
この説明では、それを省略して「File(...)」とだけ書いていた感じですね。
最初「/sdcard/」とか直接書こうとしたのですが、デバイスによって場所が異なるらしいので、正解がわかりませんでした。
こういう時はこうしましょう。という例題をいくつか見て、解決した方法がこちらです↓
↓
val fname = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(System.currentTimeMillis()) + ".jpg" val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),fname) //Log.e(TAG, file.toString()) val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
下の行から説明していきます。
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
最初「File(...)」があったところを「file」に書き換えました。
これは必要な情報を、fileという変数に入れたからです。エラーにはなりません。
じゃあfileに何を入れたか。
それが真ん中の行です。
val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),fname)
fileの中にはFile関数を使って、ファイルのフルパスを入れてます。
記述方法は File(ディレクトリ,ファイル名)という記述です。
これに当てはめると、
ディレクトリ=「Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)」
ファイル名=「fname」
となります。
ディレクトリですが、「 Environment.getExternalStoragePublicDirectory 」という書き方をすると、共有ディレクトリを指すようです。
そして「DIRECTORY_PICTURE」は画像を保存する場所を指しています。
つまり「共有ディレクトリの画像を保存する場所」に保存するようにしてるわけですね。
次にファイル名ですが、fnameという変数にしており、fnameは
val fname = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(System.currentTimeMillis()) + ".jpg"
で作成しています。
現在の日時を、yyyyMMdd_HHmmssという形式に変換して、拡張子.jpgをつけると。
これにより、ファイル名の重複が避けられるという仕組みですね。
ちなみに「SimpleDateFormat」「Locale」「Environment」全部インポートできましたが、
「SimpleDataFormat」をインポートするときに、上記のような選択肢が出てきました。
とりあえず上を選んでみたところ、特に問題なかったのでそのままとしています。
出来上がったのがこちら↑
「getExternalStoragePublicDirectory」の部分に、取り消し線が引かれているのがわかるでしょうか。
何か問題があるのかなと思ったのですが、古いAndroidバージョン用の記載であり、新しいAndroidバージョン用の記載じゃないよ。
という事らしいんです。
新しい記述にするべきなのかもしれないですが、今は「Android 7.1.2」で使いたいのと、この場合、「古い」の方になるらしいので、これでいきます。
残るはこの「cameraExecutor」です。
「Executor」って、並列処理させるために、他の仕組みに任せる。みたいなそういう使い方をするらしいです。
ここは写真を保存する部分の処理なんですが、「写真をプレビューする」はメイン処理。「写真を保存する」はメイン処理とは無関係に、保存だけの処理を別で行う。みたいな。
そんなふわっとしたイメージです。
で、ここには、その「他に任せる」を、誰に任せるのかを指定するような?
結果論。こうしました↓
val cameraExecutor = Executors.newSingleThreadExecutor()
該当行のひとつ前に、「cameraExecutor」は「Executors.newSingleThreadExecutor()」だよ。
っていうものを追加。
newって書いてあるので、新しいシングルスレッドのエグゼキューターを指定したような感じ。
あっ、「Executors」はインポートできましたので…
private fun bindPreview(cameraProvider : ProcessCameraProvider) { val preview : Preview = Preview.Builder() .build() val cameraSelector : CameraSelector = CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK) .build() val kanadeFinder: PreviewView = findViewById(R.id.viewFinder) preview.setSurfaceProvider(kanadeFinder.surfaceProvider) val imageCapture = ImageCapture.Builder() //.setTargetRotation(view.display.rotation) .build() var camera = cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageCapture, preview) //var camera = cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, preview) val fname = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(System.currentTimeMillis()) + ".jpg" val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),fname) //Log.e(TAG, file.toString()) val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build() val cameraExecutor = Executors.newSingleThreadExecutor() imageCapture.takePicture(outputFileOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback { override fun onError(error: ImageCaptureException) { // insert your code here. } override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { // insert your code here. } } ) }
これで、「MainActivity.kt」への変更は終了しました。
カメラへのアクセスには、カメラへのアクセス権限が必要でした。
じゃあ画像フォルダへのアクセスには…
次回へ続きます(笑
最後までお読みいただきありがとうございました。
気になることがあったら、コメント頂けると嬉しいです。
自主学習も兼ねて記事にするかもしれません。