【问题标题】:ActivityResultContracts TakePicture it is always returning false as a resultActivityResultContracts TakePicture 结果总是返回 false
【发布时间】:2023-02-09 05:35:37
【问题描述】:

我正在使用 Jetpack Compose,当我调用用相机拍照的方法时,ActivityResultContracts.TakePicture 的结果始终为 false。

示例代码:

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun SomeScreen() {
    val photoUri by remember { mutableStateOf(value = Uri.EMPTY) }

    val cameraLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.TakePicture(),
        onResult = { success ->
            if (success) {
                println("success")
                println("photo uri: $photoUri")
            } else println("result failed")
        }
    )

    val cameraPermissionState = rememberPermissionState(
        permission = Manifest.permission.CAMERA,
        onPermissionResult = { granted ->
            if (granted) cameraLauncher.launch(photoUri)
            else print("camera permission is denied")
        }
    )

    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = cameraPermissionState::launchPermissionRequest) {
            Text(text = "Take a photo with Camera")
        }
    }
}

我使用 accompanist-permissions 库来简化它,打开相机应用程序并拍照的部分显然工作正常,但 cameraLauncher 的结果总是错误的......

谁能指导我解决这个问题?

【问题讨论】:

    标签: kotlin android-jetpack-compose android-camera


    【解决方案1】:

    您的代码的问题是您将 empty Uri 传递给 launch,而相机应用程序无法将图像保存在 Uri 中。如果您打开 TakePicture 类或将鼠标光标放在它上面,您将看到以下信息:

    一个 ActivityResultContract 来拍照保存到 提供的内容-Uri。如果图像被保存到 给出了 Uri。

    换句话说,TakePicture类不会自动为你创建一个File,你必须自己创建一个File并提供Uri。

    我将假设一个简单的场景,您想要为应用程序中的一些临时任务拍照。为了实现这个目标,您需要了解您的代码中缺少的一些步骤:

    1. 由于您需要创建 File 并将其公开给相机应用程序,因此您需要使用 File Provider 创建规则并在 Manifest 文件中声明它。
    2. 创建File 并使用FileProvider 公开其Uri 的函数。

      让我们从file_paths.xml开始(里面资源/xml):

      <?xml version="1.0" encoding="utf-8"?>
      <paths>
          <cache-path
              name="cache_pictures"
              path="/" />
      
      </paths>
      

      按照临时保留文件的想法,我在这里使用cache-path。

      在 Manifest 文件中:

      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android">
      
          <application ...>
      
              <activity .../>
      
              <provider
                  android:name="androidx.core.content.FileProvider"
                  android:authorities="${applicationId}.provider"
                  android:exported="false"
                  android:grantUriPermissions="true">
                  <meta-data
                      android:name="android.support.FILE_PROVIDER_PATHS"
                      android:resource="@xml/file_paths" />
              </provider>
      
          </application>
      
      </manifest>
      

      用于创建 File 并返回 Uri 的扩展:

      fun Context.createTempPictureUri(
          provider: String = "${BuildConfig.APPLICATION_ID}.provider",
          fileName: String = "picture_${System.currentTimeMillis()}",
          fileExtension: String = ".png"
      ): Uri {
          val tempFile = File.createTempFile(
              fileName, fileExtension, cacheDir
          ).apply {
              createNewFile()
          }
      
          return FileProvider.getUriForFile(applicationContext, provider, tempFile)
      }
      

      缓存文件夹在此示例中与cacheDir一起使用。如果在这里更改为filesDir,请务必将file_paths.xml上的cache-path更改为files-path。

      现在在Composable Screen你可以有这样的东西:

      @OptIn(ExperimentalPermissionsApi::class)
      @Composable
      fun SomeScreen() {
          val context = LocalContext.current
          var currentPhotoUri by remember { mutableStateOf(value = Uri.EMPTY) }
          var tempPhotoUri by remember { mutableStateOf(value = Uri.EMPTY) }
      
          val cameraLauncher = rememberLauncherForActivityResult(
              contract = ActivityResultContracts.TakePicture(),
              onResult = { success ->
                  if (success) currentPhotoUri = tempPhotoUri
              }
          )
      
          val cameraPermissionState = rememberPermissionState(
              permission = Manifest.permission.CAMERA,
              onPermissionResult = { granted ->
                  if (granted) {
                      tempPhotoUri = context.createTempPictureUri()
                      cameraLauncher.launch(tempPhotoUri)
                  } else print("camera permission is denied")
              }
          )
      
          Column(
              modifier = Modifier.fillMaxSize(),
              verticalArrangement = Arrangement.Center,
              horizontalAlignment = Alignment.CenterHorizontally
          ) {
              AnimatedVisibility(visible = currentPhotoUri.toString().isNotEmpty()) {
                  AsyncImage(
                      modifier = Modifier.size(size = 240.dp),
                      model = currentPhotoUri,
                      contentDescription = null
                  )
              }
      
              Button(onClick = cameraPermissionState::launchPermissionRequest) {
                  Text(text = "Take a photo with Camera")
              }
          }
      }
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-05
      • 1970-01-01
      • 1970-01-01
      • 2016-07-20
      • 2021-11-05
      • 2018-11-05
      相关资源
      最近更新 更多