安卓kotlin中实现相机拍摄和文件选择
需要用到的技术
- registerForActivityResult
- ActivityResultContracts
- Intent
- contentResolver
相机拍摄
相机拍有两种方式可以实现,
第一种是通过 ActivityResultContracts.TakePicture 但是返回的图片会被压缩 他的使用方式和我们等下文件选择一样的逻辑
第二种是通过 Intent 来注册动作唤起,好处是拿到的是原图,这里我们使用第二种方式
第1步 清单配置和临时文件配置
既然想拿到无损的原图我们就需要让拍照的时候把图片保存在临时目录去
拍完了再去临时目录读取出来展示
在 mainifset 配置 provider 允许我们去读取手机文件
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.cameraalbmtest.fileprovider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filr_paths" >
</meta-data>
</provider>
通过 AI 翻译上面的属性 方便我们更清晰学习每一个属性
android:name: 指定了 FileProvider 的类名,这里是 androidx.core.content.FileProvider,它是 AndroidX 库中提供的 FileProvider 类。
android:authorities: 指定了 FileProvider 的授权信息,用于标识这个 FileProvider。这个值应该是一个唯一的字符串,一般采用应用的包名 + “.fileprovider” 的形式。在这个例子中,授权信息是 com.example.cameraalbmtest.fileprovider。
android:exported: 指定了是否允许其他应用通过 Intent 访问这个 FileProvider。设置为 false 表示不允许其他应用访问,只能在你的应用内部使用。
android:grantUriPermissions: 指定了是否允许 FileProvider 授予 URI 权限。设置为 true 表示允许 FileProvider 授予 URI 权限,这样其他应用就可以使用这个 URI 访问文件。
: 这个元素用于指定 FileProvider 需要读取的 XML 配置文件,以定义文件共享的路径。在这个例子中,指定了 android.support.FILE_PROVIDER_PATHS 这个 meta-data,并通过 @xml/filr_paths 引用了一个 XML 文件,其中包含了文件共享的路径配置。
在xml目录里面创建一个 filr_paths.xml 配置文件
这个配置会被 上面的 provider 的 meta-data 标签加载
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="/" />
</paths>
通过 AI 翻译上面的属性 方便我们更清晰学习每一个属性
- 这个 XML 文件通常用于在 Android 应用中配置 FileProvider。FileProvider 是一个用于在应用间共享文件的特殊 ContentProvider,它允许应用通过 URI 共享文件,同时提供了安全性和权限控制。
- 这个 元素定义了一个路径,它指定了一个外部存储路径,用于存储照片文件。具体来说,它指定了一个名为 “my_images” 的逻辑名称(可以自定义),对应的实际路径是根目录 “/”。
- 这个 XML 文件告诉 FileProvider 在应用间共享文件时,可以使用这个路径来提供文件的访问。通常情况下,这种配置用于在应用中通过 FileProvider 共享照片文件,特别是在 Android 7.0(API 级别 24)及更高版本中,由于严格的文件权限限制,直接通过 file:// URI 共享文件可能会导致权限问题,因此使用 FileProvider 是更安全可靠的方式。
第二步 唤起相机
- 定义所需的变量
lateinit var imageUri: Uri
lateinit var outputImage: File
private var mCameraLauncher: ActivityResultLauncher<Intent>? = null;
- 注册 registerForActivityResult 事件,registerForActivityResult文档请自行百度
mCameraLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult(),
ActivityResultCallback {
// 在这里面接收返回
cameraResult(it);
}
)
- 启动拍照
// 创建一个File 对象存储拍摄的照片
outputImage = File(externalCacheDir, "test.jpg")
// 文件存在我们就给他删了 每次都重新创建
if (outputImage.exists()) {
outputImage.delete()
}
outputImage.createNewFile()
// 如果当前sdk版本大于安卓7
imageUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(
this,
"com.example.cameraalbmtest.fileprovider",
outputImage
)
} else {
Uri.fromFile(outputImage)
}
// 创建启动相机意图
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
// 这里是为了确保有应用可以处理我们这个 intent 意图
if (intent.resolveActivity(packageManager) != null) {
mCameraLauncher!!.launch(intent);
}
- 定义拍照结果函数
// 拍照返回结果
private fun cameraResult(result: ActivityResult) {
if (result.resultCode == Activity.RESULT_OK) {
val bitmap = BitmapFactory.decodeStream(
contentResolver.openInputStream(imageUri)
)
image.setImageBitmap(bitmap)
}
}
到这里我们就实现了调用系统相机拍照,并且拿到拍照结果
文件选择
文件选择就比较简单了,通过 registerForActivityResult 和 ActivityResultContracts.GetContent() 就可以实现
所以我们一定要熟悉了解registerForActivityResult 和 ActivityResultContracts
第1步 注册启动事件
// 注册一个文件启动选择事件
mGalleryLauncher = registerForActivityResult(
ActivityResultContracts.GetContent()
) {
it?.let {
galleryResult(it);
}
}
第2步 编写返回数据函数
我这里只处理了图片 你们也可以看你们业务而处理
// 选择文件返回结果
private fun galleryResult(uri: Uri?) {
if (uri != null) {
val mimeType = contentResolver.getType(uri)
when {
mimeType?.startsWith("image/") == true -> {
// 选择的是图片文件
image.setImageURI(uri)
}
mimeType?.startsWith("audio/") == true -> {
// 选择的是音频文件
println("Selected file is an audio")
}
mimeType?.startsWith("video/") == true -> {
// 选择的是视频文件
println("Selected file is a video")
}
else -> {
// 其他类型的文件
println("Selected file is of unknown type")
}
}
}
}
registerForActivityResult 知识学习
- registerForActivityResult 是用于注册 Activity Result 的一个方法,它可以让你在 Fragment 或者 Activity 中接收来自其他组件(比如另一个 Activity 或者 Fragment)返回的结果。在 Kotlin 中,通常用于替代 startActivityForResult 和 onActivityResult 方法。。
- registerForActivityResult 方法的参数是一个 ActivityResultContract 对象和一个 lambda 表达式,用于处理结果。ActivityResultContract 是一个抽象类,它定义了两个泛型参数,分别是输入类型和输出类型。这个泛型参数决定了你可以传递给另一个组件的数据类型以及从另一个组件返回的数据类型
- 在使用 registerForActivityResult 方法时,你需要实例化一个 ActivityResultContract 对象,并重写它的两个方法:createIntent 和 parseResult。createIntent 方法用于创建启动另一个组件所需的 Intent 对象,而 parseResult 方法用于解析从另一个组件返回的结果,并将结果传递给 lambda 表达式进行处理。
- 具体来说,ActivityResultContract 的实现类会根据具体的需求而定。例如,如果你需要启动一个 Activity 并获取该 Activity 返回的结果,你可以使用 ActivityForResultContract;如果你需要获取权限请求的结果,你可以使用 RequestPermissionContract 等等。
- 总的来说,registerForActivityResult 方法使得处理 Activity 结果变得更加简洁和灵活,并且可以更好地与 Kotlin 的特性结合使用。
ActivityResultContract 知识学习
- ActivityResultContract 是一个抽象类,用于定义两个泛型参数,分别是输入类型和输出类型。它定义了两个方法,createIntent 和 parseResult,这两个方法分别用于创建启动另一个组件所需的 Intent 对象和解析从另一个组件返回的结果。
- 具体来说,ActivityResultContract 的实现类决定了你可以传递给另一个组件的数据类型以及从另一个组件返回的数据类型。以下是一些常用的 ActivityResultContract 的实现类:
- ActivityForResultContract<Input, Output>: 用于启动另一个 Activity 并获取该 Activity 返回的结果。它的输入类型通常是 Intent,输出类型通常是 ActivityResult。
- RequestPermissionContract<String, Boolean>: 用于请求权限,并返回是否被授权。它的输入类型是权限名称(通常是 String 类型),输出类型是 Boolean 值。
- ActivityResultContracts.GetContent: 用于从设备的文件系统中获取内容,例如照片、视频等。它的输入类型是无,输出类型是 Uri。
- ActivityResultContracts.TakePicture: 用于拍摄照片。它的输入类型是无,输出类型是 Uri。
- ActivityResultContracts.SendEmail: 用于发送电子邮件。它的输入类型是 Email 类型,输出类型是 Boolean 值,表示是否发送成功。
- ActivityResultContracts.Dial: 用于拨打电话。它的输入类型是电话号码,输出类型是 Boolean 值,表示是否成功拨打电话。