kotlin ActivityResultContracts获取图片,因此始终返回false

gkl3eglg  于 2023-02-09  发布在  Kotlin
关注(0)|答案(1)|浏览(358)

我正在使用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的结果总是错误的...
有人能指导我解决这个问题吗?

axr492tv

axr492tv1#

您的代码问题在于,您向launch传递了一个empty Uri,而相机应用无法将图像保存在该Uri中。如果您打开TakePicture类或将鼠标光标放在该类上,您将看到以下信息:
拍摄图片并将其保存到提供的content-Uri的ActivityResultContract。如果图像已保存到给定的Uri,则返回true。
换句话说,TakePicture类不会自动为您创建File,您必须自己创建File并提供Uri
我将假设一个简单的场景,你想为应用程序中的一些临时任务拍照。为了实现这个目标,你需要了解代码中缺少的一些步骤:
1.由于您需要创建File并将其暴露给Camera应用,因此需要使用File Provider创建规则并在Manifest文件中声明。
1.创建File并通过FileProvider公开其Uri的函数。
让我们从file_paths.xml(在 res/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">

    <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")
        }
    }
}

相关问题