AndroidResultContracts.TakePicture返回Boolean而不是Bitmap

如何解决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中,我执行了以下操作:

  1. 定义了imageUri: Uri?,该值将设置为TakePicture()合同所拍摄图像的uri。
  2. 已实施onTakePhotoClick(),以在启动TakePicture()合约之前检查必要的相机权限。
  3. 定义了takePictureRegistration: ActivityResultLauncher,它将实际启动在设备上拍照的请求。当isSuccess返回为true时,我知道先前定义的imageUri现在引用了我刚拍摄的照片。
  4. 仅为代码重用而定义了takePicture: Runnable。请注意,传递给String方法的第二个FileProvider.getUriForFile(context,authority,file)参数需要匹配应用程序authorities中提供给<provider>的{​​{1}}属性。
  5. 为了完全透明,我还添加了代码,显示了如何使用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-permissionuses-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>

The image that was just taken by the device camera is being displayed in an ImageView in the application

,

我一直在为同样的问题苦苦挣扎,只是想出了一个(有点)站得住脚的解决方案,涉及 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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-