如何解决AndroidResultContracts.TakePicture返回Boolean而不是Bitmap
从androidx.activity版本1.2.0-alpha05开始,合同已更改为返回Boolean
而不是Bitmap
。如何使用内置https://github.com/android/testing-samples/tree/master/ui/espresso/IntentsAdvancedSample合同返回的Boolean
来访问和显示用户刚拍摄的照片?
解决方法
我正在使用
implementation 'androidx.activity:activity-ktx:1.2.0-alpha07'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha07'
这是我的完整示例代码,显示了如何使用内置的Android结果合同从应用程序中拍摄照片并将其显示在ImageView中。
注意:我的解决方案使用View Binding
MainActivity
的布局XML包括(1)一个将onTakePhotoClick
定义为onClick
事件的按钮,以及(2)和ImageView显示拍摄的照片。
<Button
android:id="@+id/take_photo_button"
style="@style/Button"
android:drawableStart="@drawable/ic_camera_on"
android:onClick="onTakePhotoClick"
android:text="@string/button_take_photo"
app:layout_constraintTop_toBottomOf="@id/request_all_button" />
...
<ImageView
android:id="@+id/photo_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@id/take_video_button" />
在我的MainActivity
中,我执行了以下操作:
- 定义了
imageUri: Uri?
,该值将设置为TakePicture()
合同所拍摄图像的uri。 - 已实施
onTakePhotoClick()
,以在启动TakePicture()
合约之前检查必要的相机权限。 - 定义了
takePictureRegistration: ActivityResultLauncher
,它将实际启动在设备上拍照的请求。当isSuccess
返回为true
时,我知道先前定义的imageUri
现在引用了我刚拍摄的照片。 - 仅为代码重用而定义了
takePicture: Runnable
。请注意,传递给String
方法的第二个FileProvider.getUriForFile(context,authority,file)
参数需要匹配应用程序authorities
中提供给<provider>
的{{1}}属性。 - 为了完全透明,我还添加了代码,显示了如何使用ActivityResultContracts.RequestPermission()向用户请求运行时权限以访问摄像机。
AndroidManifest.xml
为了完全透明, private var imageUri: Uri? = null
/**
* Function for onClick from XML
*
* Check if camera permission is granted,and if not,request it
* Once permission granted,launches camera to take photo
*/
fun onTakePhotoClick(view: View) {
if (!checkPermission(Manifest.permission.CAMERA)) {
// request camera permission first
onRequestCameraClick(callback = takePicture)
} else {
takePicture.run()
}
}
private val takePicture: Runnable = Runnable {
ImageUtils.createImageFile(applicationContext)?.also {
imageUri = FileProvider.getUriForFile(
applicationContext,BuildConfig.APPLICATION_ID + ".fileprovider",it
)
takePictureRegistration.launch(imageUri)
}
}
private val takePictureRegistration =
registerForActivityResult(ActivityResultContracts.TakePicture()) { isSuccess ->
if (isSuccess) {
mBinding.photoPreview.setImageURI(imageUri)
}
}
/**
* Function for onClick from XML
*
* Launches permission request for camera
*/
fun onRequestCameraClick(view: View? = null,callback: Runnable? = null) {
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
// update image
mBinding.iconCameraPermission.isEnabled = isGranted
val message = if (isGranted) {
"Camera permission has been granted!"
} else {
"Camera permission denied! :("
}
Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
if (isGranted) {
callback?.run()
}
}.launch(Manifest.permission.CAMERA)
}
实用工具类具有如下定义的ImageUtils
方法,并在给定上下文时返回createImageFile()
。请注意,我正在使用外部文件目录作为FileProvider的存储目录。
File?
别忘了将object ImageUtils {
lateinit var currentPhotoPath: String
@Throws(IOException::class)
fun createImageFile(context: Context): File? {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss",Locale.US).format(Date())
val storageDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"JPEG_${timeStamp}_",/* prefix */
".jpg",/* suffix */
storageDir /* directory */
).apply {
// Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = absolutePath
}
}
}
,uses-permission
和uses-feature
标签添加到provider
。
还要确保提供给AndroidManifest
的{{1}}属性与传递给authorities
方法的第二字符串参数匹配。在我的示例中,我已将软件包名称+“ .fileprovider”设置为我的权限。详细了解FileProvider
from Google's documentation。
<provider>
我的FileProvider.getUriForFile(context,file)
如下所示。因为我使用的是<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.captech.android_activity_results">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
<application
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.captech.android_activity_results.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
,所以我使用的是XML中的res/xml/file_paths
标签。
注意::如果您不使用外部文件目录,则可能要查找要在XML标记here中指定的FileProvider存储目录。
getExternalFilesDir()
结果将在ImageView中显示<external-files-path>
:
我一直在为同样的问题苦苦挣扎,只是想出了一个(有点)站得住脚的解决方案,涉及 ContentResolver。文档留下了很多想象空间。这种方法的一个主要问题是捕获的图像 uri 必须在 ActivityResultContract 外部进行管理,这似乎违反直觉,正如原始问题已经指出的那样。我不知道另一种将媒体插入图库的方法可以解决这部分问题,但我非常希望看到这种解决方案。
// Placeholder Uri
val uri: Uri? = null
// ActivityResultContract for capturing an image
val takePicture =
registerForActivityResult(contract =
ActivityResultContracts.TakePicture()) { imageCaptured ->
if (imageCaptured) {
// Do stuff with your Uri here
}
}
...
fun myClickHandler() {
// Create a name for your image file
val filename = "${getTimeStamp("yyyyMMdd_HHmmss")}-$label.jpg"
// Get the correct media Uri for your Android build version
val imageUri =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(
MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
val imageDetails = ContentValues().apply {
put(MediaStore.Audio.Media.DISPLAY_NAME,filename)
}
// Try to create a Uri for your image file
activity.contentResolver.insert(imageUri,imageDetails).let {
// Save the generated Uri using our placeholder from before
uri = it
// Capture your image
takePicture.launch(uri)
} ?: run {
// Something went wrong
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。