我正在使用Jetpack Compose,当我调用用相机拍照的方法时,ActivityResultContracts.TakePicture的结果总是假的.

示例代码:

@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的结果总是错误的……

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

推荐答案

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

一个ActivityResultContract来拍摄照片,并将其保存到 提供的内容-URI.如果图像保存到 给定的URI.

换句话说,TakePicture类不会自动为您创建File,您将不得不自己创建File并提供Uri.

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

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

让我们从file_paths.xml开始(res/xml内部):

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

</paths>

出于暂时保存文件的 idea ,我在这里使用了cache-path.

Manifest文件中:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.CAMERA" />

    <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,一定要在cache-path号公路上从file_paths.xml改到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()) {
            // from coil library 
            AsyncImage(
                modifier = Modifier.size(size = 240.dp),
                model = currentPhotoUri,
                contentDescription = null
            )
        }

        Button(onClick = cameraPermissionState::launchPermissionRequest) {
            Text(text = "Take a photo with Camera")
        }
    }
}

Kotlin相关问答推荐

将基于注册的服务转换为流

为什么onEach不是挂起函数,而Collect是?

在Kotlin项目中使用Free Fair AspectJ插件(使用Gradle)

Kotlin - 什么时候和什么时候不喜欢内联函数,为什么?

扩展属性委托给实例属性

为何Kotlin标准库中的AND和OR函数不能像&&和||一样进行短路运算?

如何将 `when` 与 2 个密封类一起使用并获取内部值?

Kotlin 可空泛型

在 Kotlin 中 import 如何找到文件路径?

T except one class

参考 Kotlin 中的 Java 接口静态字段

如何用 kotlin 打包 List

如何在 kotlin 中生成 json 对象?

如何使用 Kotlin Coroutines 使 setOnClickListener debounce 1 秒?

Hilt Activity 必须附加到 @AndroidEntryPoint 应用程序

面临一些未知问题一些后端jvm内部错误

Kotlin中的属性(properties)和参数(parameters)有什么区别?

Android studio,构建kotlin时出现奇怪错误:生成错误代码

如何在 Kotlin 中定义新的运算符?

如何在 Intellij Idea 中运行 Kotlin 函数