Skip to content

这篇文章是这个系列的最后一篇,前面一篇讲到如何使用CameraX对预览照片进行截图,这篇文章会在上一篇代码的基础上,来分享下如何从图库中选中一张照片,上篇文章中有下面这段代码:

kotlin
val emptyImageUri = Uri.parse("file://dev/null")
Image(
  modifier = Modifier.fillMaxSize(),
  painter = rememberAsyncImagePainter(imageUri),
  contentDescription = "Captured image"
)
val emptyImageUri = Uri.parse("file://dev/null")
Image(
  modifier = Modifier.fillMaxSize(),
  painter = rememberAsyncImagePainter(imageUri),
  contentDescription = "Captured image"
)

所以如果要显示图片,我们只要从图库中选中一张图片,然后转换成URI的形式就可以。

Manifest权限

首先我们要向AndroidManifest.xml文件加入下面的媒体访问权限:

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

图库图片

之后我们还需要一个函数,来打开图库:

kotlin
@Composable
fun GallerySelect(
    modifier: Modifier = Modifier,
    onImageUri: (Uri) -> Unit = { }
) {
    val context = LocalContext.current
    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent(),
        onResult = { uri: Uri? ->
            onImageUri(uri ?: EMPTY_IMAGE_URI)
        }
    )
@Composable
fun GallerySelect(
    modifier: Modifier = Modifier,
    onImageUri: (Uri) -> Unit = { }
) {
    val context = LocalContext.current
    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent(),
        onResult = { uri: Uri? ->
            onImageUri(uri ?: EMPTY_IMAGE_URI)
        }
    )

这里我们申明一个activity的Launcher,使用SideEffect方式来调用:

kotlin
SideEffect {
    launcher.launch("image/*")
}
SideEffect {
    launcher.launch("image/*")
}

这将会过滤显示所有MIME为image类型的文件,也就是所有图片,在Android 10以上,需要申请权限,我们将代码改造下,完整如下:

kotlin
@Composable
fun GallerySelect(
    modifier: Modifier = Modifier,
    onImageUri: (Uri) -> Unit = { }
) {
    val context = LocalContext.current
    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent(),
        onResult = { uri: Uri? ->
            onImageUri(uri ?: EMPTY_IMAGE_URI)
        }
    )

    @Composable
    fun LaunchGallery() {
        SideEffect {
            launcher.launch("image/*")
        }
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        Permission(
            permission = Manifest.permission.ACCESS_MEDIA_LOCATION,
            rationale = "You want to read from photo gallery, so I'm going to have to ask for permission.",
            permissionNotAvailableContent = {
                Column(modifier) {
                    Text("O noes! No Photo Gallery!")
                    Spacer(modifier = Modifier.height(8.dp))
                    Row {
                        Button(
                            modifier = Modifier.padding(4.dp),
                            onClick = {
                                context.startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
                                    data = Uri.fromParts("package", context.packageName, null)
                                })
                            }
                        ) {
                            Text("Open Settings")
                        }
                        // If they don't want to grant permissions, this button will result in going back
                        Button(
                            modifier = Modifier.padding(4.dp),
                            onClick = {
                                onImageUri(EMPTY_IMAGE_URI)
                            }
                        ) {
                            Text("Use Camera")
                        }
                    }
                }
            },
        ) {
            LaunchGallery()
        }
    } else {
        LaunchGallery()
    }
}
@Composable
fun GallerySelect(
    modifier: Modifier = Modifier,
    onImageUri: (Uri) -> Unit = { }
) {
    val context = LocalContext.current
    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent(),
        onResult = { uri: Uri? ->
            onImageUri(uri ?: EMPTY_IMAGE_URI)
        }
    )

    @Composable
    fun LaunchGallery() {
        SideEffect {
            launcher.launch("image/*")
        }
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        Permission(
            permission = Manifest.permission.ACCESS_MEDIA_LOCATION,
            rationale = "You want to read from photo gallery, so I'm going to have to ask for permission.",
            permissionNotAvailableContent = {
                Column(modifier) {
                    Text("O noes! No Photo Gallery!")
                    Spacer(modifier = Modifier.height(8.dp))
                    Row {
                        Button(
                            modifier = Modifier.padding(4.dp),
                            onClick = {
                                context.startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
                                    data = Uri.fromParts("package", context.packageName, null)
                                })
                            }
                        ) {
                            Text("Open Settings")
                        }
                        // If they don't want to grant permissions, this button will result in going back
                        Button(
                            modifier = Modifier.padding(4.dp),
                            onClick = {
                                onImageUri(EMPTY_IMAGE_URI)
                            }
                        ) {
                            Text("Use Camera")
                        }
                    }
                }
            },
        ) {
            LaunchGallery()
        }
    } else {
        LaunchGallery()
    }
}

之后我们修改下调用代码:

kotlin
class MainActivity : ComponentActivity() {
    @OptIn(ExperimentalPermissionsApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            PhotoAppWithCameraX_ComposeTheme {
                // A surface container using the 'background' color from the theme
                Surface(modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background) {
                    var imageUri by remember { mutableStateOf(EMPTY_IMAGE_URI) }
                    if (imageUri != EMPTY_IMAGE_URI) {
                        Box() {
                            Image(
                                modifier = Modifier.fillMaxSize(),
                                painter = rememberImagePainter(imageUri),
                                contentDescription = "Captured image"
                            )
                            Button(
                                modifier = Modifier.align(Alignment.BottomCenter),
                                onClick = {
                                    imageUri = EMPTY_IMAGE_URI
                                }
                            ) {
                                Text("Remove image")
                            }
                        }
                    } else {
                        var showGallerySelect by remember { mutableStateOf(false) }
                        if (showGallerySelect) {
                            GallerySelect(
                                onImageUri = { uri ->
                                    showGallerySelect = false
                                    imageUri = uri
                                }
                            )
                        } else {
                            Box() {
                                CameraCapture(
                                    onImageFile = { file ->
                                        imageUri = file.toUri()
                                    }
                                )
                                Button(
                                    modifier = Modifier
                                        .align(Alignment.TopCenter)
                                        .padding(4.dp),
                                    onClick = {
                                        showGallerySelect = true
                                    }
                                ) {
                                    Text("Select from Gallery")
                                }
                            }
                        }
                    }

                }
            }
        }
    }
}

val EMPTY_IMAGE_URI: Uri = Uri.parse("file://dev/null")
class MainActivity : ComponentActivity() {
    @OptIn(ExperimentalPermissionsApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            PhotoAppWithCameraX_ComposeTheme {
                // A surface container using the 'background' color from the theme
                Surface(modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background) {
                    var imageUri by remember { mutableStateOf(EMPTY_IMAGE_URI) }
                    if (imageUri != EMPTY_IMAGE_URI) {
                        Box() {
                            Image(
                                modifier = Modifier.fillMaxSize(),
                                painter = rememberImagePainter(imageUri),
                                contentDescription = "Captured image"
                            )
                            Button(
                                modifier = Modifier.align(Alignment.BottomCenter),
                                onClick = {
                                    imageUri = EMPTY_IMAGE_URI
                                }
                            ) {
                                Text("Remove image")
                            }
                        }
                    } else {
                        var showGallerySelect by remember { mutableStateOf(false) }
                        if (showGallerySelect) {
                            GallerySelect(
                                onImageUri = { uri ->
                                    showGallerySelect = false
                                    imageUri = uri
                                }
                            )
                        } else {
                            Box() {
                                CameraCapture(
                                    onImageFile = { file ->
                                        imageUri = file.toUri()
                                    }
                                )
                                Button(
                                    modifier = Modifier
                                        .align(Alignment.TopCenter)
                                        .padding(4.dp),
                                    onClick = {
                                        showGallerySelect = true
                                    }
                                ) {
                                    Text("Select from Gallery")
                                }
                            }
                        }
                    }

                }
            }
        }
    }
}

val EMPTY_IMAGE_URI: Uri = Uri.parse("file://dev/null")

到这里应该可以从图库中选图片了,这样一个图片APP的一些基础功能都算演示完了,后续就是根据自己的需要进行各种扩展了,完整的代码可以访问:AndroidCameraXWithJetpackCompose