ファイル選択orカメラ撮影で画像を選択できるようにしたい
こういうやつ。
カメラで撮った写真を使う場合と端末のファイルを利用する場合でそれぞれ単体の実装は以下の記事を参照。
実装
自作ActivityResultContract
ボトムシートとしてファイル選択、カメラ撮影から選択できるようにするためのActivityResultContract
を作成する。
class ChooserActivityResultContract(private val context: Context?) : ActivityResultContract<Unit, Uri?>() {
// 端末のファイルを選択するためのIntent
private val getContentIntent
get() = context?.let {
ActivityResultContracts.OpenDocument().createIntent(
it,
arrayOf("image/jpeg", "image/png")
)
}
// カメラ撮影するためのIntent
private val takePictureIntent
get() = context?.let {
ActivityResultContracts.TakePicture().createIntent(
it,
cacheUri ?: Uri.EMPTY
)
}
// カメラ撮影したファイルの保存先を表すUri
private var cacheUri : Uri? = null
override fun createIntent(context: Context, input: Unit): Intent {
cacheUri = FileProvider.getUriForFile(
context,
"${BuildConfig.APPLICATION_ID}.provider",
context.cacheDir.toPath().resolve("cache.jpg").toFile())
// OpenDocumentとTakePictureから選択できるChooserを起動する
return Intent.createChooser(getContentIntent, "選択").apply {
// 端末にカメラがあればTakePictureの選択肢を追加
val hasCameraFeature = context.packageManager
?.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
?: false
if (hasCameraFeature) {
putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(takePictureIntent))
}
}
}
override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
return if (context == null || resultCode != Activity.RESULT_OK) {
null
} else {
// OpenDocumentで選択した場合は intent?.data に、
// TakePicutreでカメラ撮影して保存されたファイルはcacheUriに、
// content://〜 形式のUriが入っている
intent?.data ?: cacheUri
}
}
}
cacheディレクトリカメラで撮影した画像ファイルを保存できるようにする
自作ActivityResultContract
でカメラで撮影したファイルをキャッシュフォルダに保存するようにしたので、
キャッシュフォルダにカメラアプリがファイルを保存できるように許可する必要がある。
参考: 端末で写真を撮って表示する
AndroidManifest.xml
にcache
フォルダへのファイルの保存を許可設定を追加する。
<application ...>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider" />
</provider>
</application>
許可するフォルダは以下の設定で行なう。
<paths>
<cache-path name="cache" path="." />
</paths>
Fragmentで自作ActivityResultContractを呼び出す
自作ActivityResultContract
からregisterForActivityResult
でlauncherを作成し、
ボタンクリック時に起動する。
class FirstFragment : Fragment() {
// 自作ActivityResultContractから作成されるlauncherを保存するフィールド
private var chooser: ActivityResultLauncher<Unit>? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
...
// 自作ActivityResultContractからlauncherを作成する
chooser = registerForActivityResult(ChooserActivityResultContract(context)) { result ->
val uri = result ?: return@registerForActivityResult
val context = context ?: return@registerForActivityResult
viewModel.loadImage(uri, context)
}
...
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// ボタンクリック時に立ち上げるようにする
binding.button.setOnClickListener {
chooser?.launch(Unit)
}
}
}
動作確認
\確かな知識を身に着けたい、Androidアプリ開発を学びたい人にオススメ!/