简单的Image Picker:使用Jetpack Compose无需权限申请

news2025/6/28 15:07:12

简单的Image Picker:使用Jetpack Compose无需权限申请

学习如何在Android应用中轻松选择、存储和加载本地图像,无需繁琐的权限处理。
作为一名Android开发者,我知道在应用中实现本地图像选择时处理权限可能会让人感到沮丧。这就是为什么我想与你分享一种使用Kotlin和Jetpack Compose简化本地图像选择的方法。在本教程中,我将向你展示如何选择图像、将其URI存储在本地数据库中,并在用户下次打开应用程序时加载图像,而无需繁琐的权限处理。让我们开始吧!

创建Image Picker composable

首先,您需要添加coil依赖项,以便使用URI加载图像(您可以使用您熟悉的任何其他库),然后同步您的项目,以确保成功添加了依赖项。

implementation("io.coil-kt:coil-compose:2.4.0")
@Composable
fun ImagePicker() {
    var imageUri: Uri? by remember {
        mutableStateOf(null)
    }   
    val launcher =
        rememberLauncherForActivityResult(contract = ActivityResultContracts.OpenDocument()) {
            it?.let { uri ->
              imageUri = uri
            }
        }
    Column(
        modifier = Modifier
            .fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically)
    ) {
        AsyncImage(
            model = imageUri,
            contentDescription = null,
            modifier = Modifier
                .size(200.dp),
            contentScale = ContentScale.Crop
        )
        Button(
            onClick = {
                launcher.launch(arrayOf("image/*"))
            }
        ) {
            Text(text = "Pick Image")
        }
    }
}

在提供的代码中,我们定义了ImagePicker组合函数,负责显示用户界面元素并处理图像选择逻辑。

首先,我们声明一个可变状态变量imageUri,用于保存所选图像的URI。

接下来,我们使用rememberLauncherForActivityResult定义了一个launcher。此launcher负责启动图像选择器活动并接收结果。我们使用ActivityResultContracts.OpenDocument()接口,允许用户从设备存储中选择文档(在本例中为图像文件)。当用户选择图像时,调用传递给launcher的lambda函数,并将选定的URI赋值给imageUri变量。

然后,我们定义了一个简单的界面来使用coil显示加载的图像,使用AsyncImage组合来显示选定的图像。我们将imageUri作为model参数传递,当imageUri更改时,它会自动加载和显示图像。为简单起见,contentDescription设置为null。

在图像下方,我们有一个按钮,触发图像选择过程。当点击按钮时,通过调用launcher.launch(arrayOf("image/*"))来启动launcher。这将打开图像选择器活动,允许用户从设备图库中选择图像。

实现Image Url本地存储

为了简单起见,我们将使用DataStore来快速存储和加载图像URI,但你可以使用Room或其他你熟悉的本地存储方法。要使用DataStore,你需要添加以下依赖:

implementation 'androidx.datastore:datastore-preferences:1.0.0'

然后,我们将创建一个类来处理图像 URI 的存储和检索操作:

class StoreData {
    private val Context.storeData: DataStore<Preferences> by preferencesDataStore(name = "data")
    suspend fun storeImage(context: Context, value: String) {
        context.storeData.edit { preferences ->
            preferences[stringPreferencesKey("image")] = value
        }
    }
    suspend fun getImage(context: Context): Flow<String?> {
        return context.storeData.data.map {
            preferences ->
            preferences[stringPreferencesKey("image")]
        }
    }
}

首先,我们使用 preferencesDataStore 委托声明了一个类型为 DataStore<Preferences>storeData 属性。该属性用于访问与提供的名称(“data”)相关联的 DataStore 实例。DataStore 允许我们持久地存储键值对。

接下来,我们有一个挂起函数 storeImage,它接受一个 Context 和一个值(图像 URI)作为参数。在函数内部,我们使用 storeDataedit 函数来修改偏好设置。我们使用键 “image” 将图像 URI 存储在偏好设置中。

类似地,我们还有另一个挂起函数 getImage,它接受一个 Context 作为参数。该函数返回一个 Flow<String?>,即可空字符串的流。在函数内部,我们使用 storeDatadata 属性从中检索图像 URI。然后,我们通过映射偏好设置来提取与键 “image” 相关联的图像 URI 值。

通过使用这个 StoreData 类,您可以轻松地在本地存储中存储和检索图像 URI。

将Image Picker与存储整合

@Composable
fun ImagePicker() {
    var imageUri: Uri? by remember { mutableStateOf(null) }
    val context = LocalContext.current
    val dataStore = remember { StoreData() }
    val scope = rememberCoroutineScope()
    val launcher =
        rememberLauncherForActivityResult(contract = ActivityResultContracts.OpenDocument()) {
            it?.let { uri ->
                context.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION )
                scope.launch {
                    dataStore.storeImage(context, uri.toString())
                }
            }
        }
    Column(
        modifier = Modifier
            .fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically)
    ) {
        AsyncImage(
            model = imageUri,
            contentDescription = null,
            modifier = Modifier
                .size(200.dp),
            contentScale = ContentScale.Crop
        )
        Button(
            onClick = {
                launcher.launch(arrayOf("image/*"))
            }
        ) {
            Text(text = "Pick Image")
        }
    }
    LaunchedEffect(key1 = true) {
       dataStore.getImage(context).collect {
            if (it != null)
                imageUri = Uri.parse(it)
        }
    }
}

在提供的代码中,我们对 ImagePicker composable 进行了修改和添加,以将其与存储功能集成在一起。让我们逐步了解这些更改的目的。

  1. 我们在 composable 内部获取了当前上下文的访问权限。
  2. 使用 remember { StoreData() } 创建了 StoreData 类的实例。这将初始化存储类,使我们能够存储和检索图像 URI。
  3. 创建了一个协程作用域,用于启动协程。
  4. 当用户选择一个图像(不为 null)时,我们必须调用 takePersistableUriPermission 授予图像 URI 所需的读取权限。
  5. 然后,我们使用 scope.launch 启动一个协程,使用 storeImage 函数将图像 URI 存储在 dataStore 中。
  6. 使用 LaunchedEffect,它仅在初始组合时启动一次。
  7. LaunchedEffect 内部,我们使用 getImage(context).collectdataStore 中收集图像 URI 的流。如果检索到的 URI 不为 null,我们将其解析为 Uri 对象并将其分配给 imageUri

通过实现这些修改和添加,ImagePicker composable 现在与存储功能集成在一起。它允许用户选择图像,在存储中自动保存选定的图像 URI,并在重新打开应用程序时加载图像 URI,为在 Android 应用程序中处理图像提供了无缝体验。

缺点

在存储图像 URI 而没有对图像本身进行本地复制的方法中存在一个缺点,即如果原始图像被删除或从其原始位置移动,存储的 URI 将不再有效。这可能导致图像链接损坏,并在未来尝试加载图像时可能出现问题。

总结

总之,我们已经了解了如何使用 Kotlin 和 Jetpack Compose 轻松处理 Android 应用程序中的本地图像选择。通过集成用户友好的图像选择器和存储解决方案,我们消除了复杂的权限处理需求,同时实现了对图像 URI 的无缝存储和检索。这种方法简化了开发过程,增强了用户体验,使得创建能够无缝处理本地图像选择的吸引人应用程序变得更加容易。

GitHub

https://github.com/oussama-dz/LocalImagePicker

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/686110.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Juc04_阻塞队列概述、方法、实现类、Linked和Array区别、注意事项

文章目录 ①. 什么是阻塞队列②. BlockingQueue的主要方法③. BlockingQueue的实现类④. Linked和Array区别⑤. 不推荐使用快捷的线程池 ①. 什么是阻塞队列 ①.阻塞队列:从名字可以看出,它也是队列的一种,那么它肯定是一个先进先出FIFO的数据结构。与普通队列不同的是,他支持两…

Flask新手教程

Flask简介 Flask是一个轻量级的可定制框架&#xff0c;使用Python语言编写&#xff0c;较其他同类型框架更为灵活、轻便、安全且容易上手。 Flask 可以很好地结合MVC模式进行开发&#xff0c;开发人员分工合作&#xff0c;小型团队在短时间内就可以完成功能丰富的中小型网站或…

正点原子uboot分析

知识点 为终端不输出command line&#xff1a;终端输入如果变量quiet为空的话&#xff0c;整个命令都会输出。 如果变量 quiet为“ quiet_”的话&#xff0c;仅输出短版本。 如果变量 quiet为“ silent_”的话&#xff0c;整个命令都不会输出。sinclude&#xff1a;读取的文件…

Java通过JNI调用dll动态库详细步骤

目录 目标具体示例1、编写java代码&#xff1a;定义native接口2、根据java编写的native接口生成.h头文件3、使用Visual Studio编写c代码实现头文件接口并生成dll文件4、将生成的jni.dll文件放入jdk bin下5、编写java测试类&#xff0c;调用dll 附&#xff1a;问题java测试类执行…

上门家教app小程序源码开发的前景如何?

随着我国生活水平的提高&#xff0c;教育方面的问题也越来越受到家长们的重视&#xff0c;很多家庭都开始通过家教以及辅导班等方式增强学生的学业知识&#xff0c;因此家教app开发是拥有很大一部分用户市场的。那么家教app开发主要适合什么行业呢&#xff1f; 上门家教服务或…

Zabbix-客户端部署全过程

本文已收录于专栏 《中间件合集》 目录 概念说明什么是Zabbix 功能介绍配置过程1.在linux下的admin文件夹下创建zabbix文件夹2.把agent端压缩包放置到home/admin/zabbix路径下3.解压安装包4.创建zabbix日志文件5.进入到conf文件夹下&#xff0c;修改配置文件&#xff0c;与serv…

Linux系统下网络性能监控指令(iftop、nload)

文章目录 iftop参数快捷键iftop界面说明&#xff1a; nload参数示例 iftop 参数 -i 设定监测的网卡&#xff0c;如&#xff1a;# iftop -i eth1 -B 以bytes为单位显示流量(默认是bits)&#xff0c;如&#xff1a;# iftop -B -n 使host信息默认直接都显示IP&#xff0c;如&…

今天给大家安利几款非常好用的文件迁移工具

文件迁移是我们平时经常需要处理的问题之一。在日常生活中&#xff0c;我们可能需要将一些文件从电脑上的一个位置迁移到另一个位置&#xff0c;或者将文件转移到外部硬盘或云端存储空间。而为了更加高效地完成这项任务&#xff0c;使用一款好用的文件迁移工具显得尤为重要。今…

基于smardaten无代码开发解决光伏电站项目

文章目录 前言一、事故背景二、解决方案三、工具加持四、配置要点解析1、光伏导航菜单搭建2、运行监控组件布局3、员工填报表单创建4、数据接入清洗5、复杂数据展示5、地图大屏组装6、定制页面集成 五、挖掘更多惊喜1、模拟数据生成2、智能分析结果3、草图智能识别4、日期排班设…

el-dialog设置滚动条不生效记录【草稿版,待优化】

目录 前言一、scrollTop是什么&#xff1f;二、解决步骤总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 技术栈 element-plus vue3 js 记录el-dialog的弹窗中&#xff0c;通过js控制滚动条不生效。 我这次的需求是这样的&#xff0c;点击某个按钮…

SSM-Spring项目使用Tomcat:7插件运行项目注意事项

文章目录 配置Tomcat:7插件&#xff08;注意Path&#xff09;运行tomcat:7第一种方式第二种方式 测试路径&#xff08;注意&#xff09;正确请求方式&#xff08;注意路径&#xff09; 配置Tomcat:7插件&#xff08;注意Path&#xff09; 作为演示&#xff0c;我这里特别配置了…

中学生台灯怎么选比较好?精选真正适合中学生的台灯!

现在孩子的近视率很高&#xff0c;尤其是儿童青少年居多&#xff0c;从上了小学开始作业就变多了&#xff0c;经常挑起夜灯学习的&#xff0c;而中学生负担则更重。家长重视教育质量的同时也要注意孩子学习时的光线适合学习吗&#xff1f;用眼过度和不适合的光源容易导致近视&a…

Sangfor华东天勇战队:jeecg-boot登录分析

realkey获取 7evs1680077379806 可以看到代码逻辑如下&#xff0c;md5(小写验证码checkKey)&#xff0c;这里checkKey是时间戳 然后继续往下看 这里checkcode取值为空&#xff0c;这是为什么呢&#xff0c;是因为redisUtil中没有获得&#xff0c;登录的时候会存到redis中&a…

SLAM之反求运动和地图点(对极几何)

简介 前面的文章介绍了如何在已知空间点的情况下在不同坐标系中的表示&#xff08;刚体的坐标变换&#xff09;&#xff0c;以及如何将空间中的点投影到相机中生成图像&#xff0c;但是现实中的情况却是相反的情况&#xff08;空间点以及坐标系之间的变换未知&#xff09;&…

【裸机开发】定时器按键消抖(EPIT)

实际工程中&#xff0c;不能直接通过延时来消抖 ! 这里我们采用定时器来消抖&#xff0c;这也是内核处理消抖的一种方式。 目录 一、基本原理 1、延时消抖的弊端 2、定时器消抖原理 二、按键消抖实现 1、按键中断 2、定时器中断 三、附加&#xff1a;按键 / 定时器中断初…

前后端分离开发跨域问题总结

前后端分离开发跨域问题总结 一、什么是跨域访问二、解决跨域问题1、Vue前端配置代理解决跨域2、SpringBoot后端配置解决跨域2.1 跨域配置类CorsConfig&#xff08;常用&#xff09;2.2 Controller添加CrossOrigin注解2.3 添加CORS过滤器CorsFilter&#xff08;常用&#xff09…

selenium之鼠标操作

首先导入ActionChains类&#xff0c;该类可以完成鼠标移动&#xff0c;鼠标点击事件&#xff0c;键盘输入、内容菜单交互等交互行为。 from selenium.webdriver.common.action_chains import ActionChains 操作语法&#xff1a; 第一步&#xff1a;初始化ActionChains类&…

系列二、Maven下载安装配置

一、下载 链接&#xff1a;https://pan.baidu.com/s/1BvwLzAk9kRSP-daxSYe4Vw?pwdyyds 提取码&#xff1a;yyds 二、安装 第一步&#xff1a;下载安装包 第二步&#xff1a;解压至安装目录&#xff0c;例如 第三步&#xff1a;配置settings.xml&#xff08;主要配置maven本…

Open ai 开发指南:gpt接口的第一个问答机器人demo

目录 内容 Python代码 C 代码 workspace 文件 BUILD文件 Java 代码 maven文件 执行效果 内容 基于openai接口实现循环gpt问答&#xff0c;并使用一个文件将问答内容进行记录。 Python代码 # -*- coding: utf-8 -*- import openai import time from pathlib import P…

100天精通Python(可视化篇)——第93天:Pyecharts绘制多种炫酷饼图参数说明+代码实战(百分比、环形、玫瑰、内嵌、多个子图饼图)

文章目录 专栏导读1. 基础饼图add函数简单案例改变颜色 2. 百分比饼图3. 环形饼图4. 玫瑰饼图5. 内嵌环图6. 多个饼图 专栏导读 &#x1f525;&#x1f525;本文已收录于《100天精通Python从入门到就业》&#xff1a;本专栏专门针对零基础和需要进阶提升的同学所准备的一套完整…