应用程序重新启动后保存并显示通过 URI 选择的图片

2023-12-02

我的想法是在那里渲染个人资料图片并允许用户更改它。 为了保存所选图片,我正在使用SharedPreferences(将 Uri 保存为字符串)。问题是每次启动时图像都不会显示。 共享管理器检索到的值似乎是正确的,但是SubComposeAsyncImageContent无法正确显示图片。

个人资料图片可组合:

@Composable
fun ProfilePicture(
    imageUri: String?,
    size: Dp = 50.dp,
    onClick: (String) -> Unit,
) {

    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent())  { uri: Uri? ->
        onClick(uri.toString())
    }

    if (imageUri != null) {
        Log.e("ProfilePicture", imageUri)
    }

    SubcomposeAsyncImage(
        model = imageUri,
        contentDescription = "",
        modifier = Modifier.clickable { launcher.launch("image/*") }
    ) {
        val state = painter.state
        Log.e("ProfilePicState", "${state}")
        if (state is AsyncImagePainter.State.Loading || state is AsyncImagePainter.State.Error) {
            CircularProgressIndicator()
        } else {
            SubcomposeAsyncImageContent()
        }

    }
}

我们的想法是imageUri从配置文件屏幕(包含一个ProfilePicture)。配置文件屏幕从以下位置获取该值viewModel,它可以访问sharedPreferences。

ProfileScreen.kt:

@Composable
fun ProfileScreen(viewModel: ProfileViewModel) {

    var profileUri by rememberSaveable {
        mutableStateOf(viewModel.getProfilePicURI())
    }

    Log.w("ProfileScreen", profileUri)

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        ProfilePicture(
            imageUri = profileUri,
            size = 150.dp,
            onClick = {
                viewModel.onEvent(ProfileEvents.OnUpdateProfilePic(it))
                profileUri = viewModel.getProfilePicURI()
            }
        )
    }
}

最后,viewModel:

class ProfileViewModel(val preferenceManager: PreferenceManager): ViewModel() {

    fun getProfilePicURI(): String {
        return preferenceManager.getProfilePic()
    }

    fun onEvent(event: ProfileEvents) {
        when (event) {
            is ProfileEvents.OnUpdateProfilePic -> {
                // update the sharedpreference
                preferenceManager.setProfilePic(event.newUri)
                Log.e("ProfileVM", "uri stored: ${getProfilePicURI()}")

            }
        }
    }
}

如前所述,代码在应用程序内运行,即我可以更改个人资料图片,即使我返回个人资料屏幕,它也会被记住,但每次启动时,画家都无法加载图像,即使正确的资源似乎是发送。

日志如下所示:

2022-04-27 09:29:45.174 12502-12502/com.example.insurance W/ProfileScreen: content://com.android.providers.media.documents/document/image%3A96
2022-04-27 09:29:45.182 12502-12502/com.example.insurance E/ProfilePicture: content://com.android.providers.media.documents/document/image%3A96
2022-04-27 09:29:45.258 12502-12502/com.example.insurance E/ProfilePicState: Loading(painter=null)
2022-04-27 09:29:45.274 12502-12502/com.example.insurance E/ProfilePicState: Loading(painter=null)
2022-04-27 09:29:45.278 12502-12502/com.example.insurance E/ProfilePicState: Error(painter=null, result=coil.request.ErrorResult@bfc77785)

使用后退按钮退出应用程序是可行的,在游戏中,个人资料图片就在那里。通过任务任务管理器销毁它会触发错误的行为。

当我启动应用程序时,显示个人资料图片的唯一方法是选择不同的图像,即,如果我选择之前选择的图像,则不会显示。一旦我选择一个新的,它就会再次显示


最新版本的 Android 仅在当前应用程序会话期间提供对文件的访问 - 重新启动后,您将不再有权读取相同的 URI。

该文件仍然在那里:如果您再次选择它,您将获得相同的 URI,并且也将具有访问权限,但在您的情况下,相同的 URI 不会触发重组,这就是您看不到更新的原因。

即使您有权访问 URI,您也无法直接访问,您只能从以下位置获取输入流context.contentResolver。这就是 Coil 在显示它时在幕后为您所做的事情,但要复制它,您必须自己管理它。

输出文件应该位于应用程序特定的存储.

val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.GetContent()
)  { newUri: Uri? ->
    if (newUri == null) return@rememberLauncherForActivityResult

    val input = context.contentResolver.openInputStream(newUri) ?: return@rememberLauncherForActivityResult
    val outputFile = context.filesDir.resolve("profilePic.jpg")
    input.copyTo(outputFile.outputStream())
    uri = outputFile.toUri()
}

请注意,我在这里使用相同的文件名,因此如果您连续选择两个不同的图像,您将不会看到从第一个图像到第二个图像的更新,因为输出 URI 仍然相同。如何解决这个问题取决于你,例如在文件名中添加一个 UUID,这样它每次都是一个唯一的 URI,只是不要忘记删除旧的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

应用程序重新启动后保存并显示通过 URI 选择的图片 的相关文章

随机推荐

  • ansi C 对程序中外部变量的数量有限制吗?

    外部变量是指用以下语句声明的变量extern修饰符 并在程序的其他地方定义 我被告知这个限制 但在网上找不到确认或反驳 这是真的 如果可以的话 极限是多少 它通常由编译器强制执行吗 C99 标准指定了几个转换限制 5 2 4 1 其中之一是
  • 为什么使用 SETLOCAL 命令切换命令提示符当前目录的批处理文件不起作用?

    我正在命令提示符窗口中运行以下批处理脚本以转到特定文件夹 echo off SETLOCAL set ispyfolder true if not 1 py if not 1 pyfolder set ispyfolder false if
  • python pip install psycopg2安装错误

    我做了一个简单的pip install psycopg2在mac系统上 它安装得很好 但是当我尝试使用 psycopg2 时 出现错误 Reason Incompatible library version psycopg so requi
  • “无法从静态上下文引用非静态方法”背后的原因是什么? [复制]

    这个问题在这里已经有答案了 初学者最常见的错误是当您尝试 静态 使用类属性而不创建该类的实例时 它会给您留下提到的错误消息 您可以将非静态方法设置为静态 也可以创建该类的实例以使用其属性 这背后的原因是什么 我关心的不是解决方案 而是原因
  • 我应该使用静态方法还是非静态方法?

    我已经用 C 创建了一个控制台应用程序 并且有main方法 静态 我的要求是初始化 2 个计时器并分别处理 2 个方法 这些方法将被定期调用来执行某些任务 现在 我已将所有其他方法 变量设为静态 因为它们是从计时器处理程序事件调用的 由于从
  • TypeScript / Visual Studio 2012 / 编译参数

    当从 Visual Studio 2012 执行 TypeScript 编译器时 我需要生成源映射 在 Sublime Text 2 中 我只需向构建脚本添加一个额外的参数 我迷失在 VS 2012 中 构建部分似乎没有将构建参数添加到构建
  • 按钮 C# (WinForms) 中的圆角边缘

    您好 通过这里和其他网站的一些研究 我制作了一个圆边按钮 protected override void OnPaint PaintEventArgs e base OnPaint e Rectangle Rect new Rectangl
  • java堆分配多少物理内存是如何决定的?

    我有16G内存的机器 我运行一个带有参数的java应用程序 Xms9G Xmx9G 当我跑步时top命令我看到我的java进程正在执行13 8克VIRT 但只有4 6克RES PID USER PR NI VIRT RES SHR S CP
  • 尽管已安装模块,导入语句仍会使角度应用程序崩溃

    可能是什么原因import如果语法正确并且库已经安装 语句会导致错误 附图 可能是下面两个主要设置文件中的某些内容 包 json tsconfig json 我们继承了一个大型的 Angular 应用程序 并在其中安装了vega vega
  • WordPress 如何读取评论行

    在WordPress中 注释行用于查找主题摘要 插件摘要 模板名称等 例如 WordPress 如何做到这一点 使用什么代码来读取注释行 这是在函数中完成的get file data in wp includes functions php
  • 使用 Struts2 jQuery 插件填充对话框内的下拉列表

    我有一个对话框 我必须在其中填充数据库中的下拉列表 我正在使用 struts2 juery plugins 来实现对话框 此对话框在某些事件中打开 这就是我创建 Dialog 的方式
  • 使用 UIButton 传递多个标签

    好的 我有一个具体情况 我正在使用自定义类来创建一些按钮 我可以使用唯一的数字设置它们的标签属性 例如 button tag NSNumber numberWithInt 10 这在我的程序的另一部分非常有用 因为我可以访问这个独特的标签
  • 前置声明有哪些缺点?

    我想知道在可能的情况下在所有地方使用前向声明是否有任何缺点 这是如果我的标头仅包含声明的情况 据我了解 使用前向声明可以加快编译时间 但我不知道有什么缺点 Example a h Class A b h Should I use and i
  • 有没有办法确定浏览器窗口已关闭?

    如何识别浏览器的关闭按钮被点击了 来自JavaScript 事件参考 最接近的匹配似乎是 OnUnload 事件 但是 这也会捕获远离页面的导航 因此 如果用户实际单击链接 您不希望运行这些功能 不保证服务器会收到浏览器窗口已关闭的消息 例
  • setDefaultUncaughtExceptionHandler 使应用程序默默崩溃

    自定义异常处理程序 public class CustomExceptionHandler implements UncaughtExceptionHandler private Context ctx private ContentRes
  • 重新引入“未知初始字符集索引”错误?

    我有一个 Java 客户端应用程序 它连接到mysql服务器 客户端和服务器都运行在 docker 容器中 我注意到官方mysql Docker镜像最近更新了mysql服务来运行Version 8 0 1 dmr 由于此更改 我的 Java
  • 为 Windows 编写自己的 memmem

    我注意到memmemMSVC for Windows 中不可用 因此我尝试为其编写一些内容 我有以下代码 void memmem const void haystack start size t haystack len const voi
  • 如何通过VBnet(或C#)与R进行通信

    最近 我用VB net开发了一个实验应用程序 在Windows平台 当应 用程序收集数据时 我想使用R来分析数据 但我不知道如何与R沟通 换句话说 我想在我自己的应用程序中将 R 脚本发送到 R 如果有人能给我一些建议或一些参考文件 我将不
  • vba 上的用户配置文件环境

    首先 我是 vba 编码的新手 我在 Access 2013 VBA 中编写了一个表单 并创建了一个在单击按钮时生成 PDF txt 文档的函数 事情是老板希望它保存在位于的共享文件夹中 userprofile 路径状C Users
  • 应用程序重新启动后保存并显示通过 URI 选择的图片

    我的想法是在那里渲染个人资料图片并允许用户更改它 为了保存所选图片 我正在使用SharedPreferences 将 Uri 保存为字符串 问题是每次启动时图像都不会显示 共享管理器检索到的值似乎是正确的 但是SubComposeAsync