使用 Uri -> 从图像选择器显示图像可以工作,但从数据库房间不可以...(Jetpack Compose)

问题描述 投票:0回答:2

我有一个来电应用程序,允许用户从手机中提取联系人照片和铃声音频文件。 当我拿起照片时,它可以使用界面上的 URI 正确显示。在主页和联系页面上。 我将其保存在房间数据库中...

如果我关闭应用程序并重新打开它,它会从数据库获取相同的 URI。但没有显示图像。不在主页或联系页面上。 我尝试手动插入 URI 代码,但它也没有显示任何内容...还将图像加载器从

GlideImage
(另一个实现)更改为
rememberImagePainter
(另一个实现),但同样的问题... 我从数据库和图像选择器获得的 URI 看起来像这样
val uri2:Uri = "content://com.android.providers.media.documents/document/image%3A34".toUri()

拾取图像Uri的代码是这个

class GetContentActivityResult(
 private val launcher: ManagedActivityResultLauncher<String, Uri>,
       val uri: Uri?
          ) {
          fun launch(mimeType: String) {
           launcher.launch(mimeType)
        }
      }

@Composable
fun rememberGetContentActivityResult(): GetContentActivityResult {

 var uri by rememberSaveable { mutableStateOf<Uri?>(null) }

 val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent(), onResult = {

      uri = it
     })

 return remember(launcher, uri) { GetContentActivityResult(launcher, uri) }
}

我在联系页面中显示图像,如下所示

val getContent = rememberGetContentActivityResult() <-- to access the class of pickup files

if (roomViewModel.photoUri.value.isBlank()){ <-- if no image from db display the picked image using image picker 
    getContent.uri?.let {
              Image(
                modifier = Modifier
                             .align(Alignment.TopCenter)
                             .fillMaxSize(1f),
                         contentDescription = "UserImage",
                         contentScale = ContentScale.Crop,
                painter = rememberImagePainter(data = it))
                      }
         }else{

             Image(
               modifier = Modifier
                            .align(Alignment.TopCenter)
                            .fillMaxSize(1f),
                         contentDescription = "UserImage",
                         contentScale = ContentScale.Crop,
                         painter = rememberImagePainter(data = roomViewModel.photoUri.value))
            }
    }

           TextButton(
                      onClick = { getContent.launch("image/*") }, <-- launching the class to get the image URI by defining the Mime as image/*
                      modifier = Modifier.layoutId("changePhoto")
            ) {
     Text(
         text = "Change Photo",
        color = Color.Black)}

在主页内。没有图像选择器,我只是显示数据库中的数据,并且以这种方式显示图像

@OptIn(ExperimentalCoilApi::class)
@Composable
fun ProfilePicture(uri : Uri, imageSize: Dp) {
 Card(
     shape = CircleShape,
     border = BorderStroke(
     width = 2.dp,
     color = Color.Red ),
     modifier = Modifier.padding(16.dp),
     elevation = 4.dp) {

 val uri2:Uri = "content://com.android.providers.media.documents/document/image%3A34".toUri() //<-- i tried to load the image this way also it didn't work.

 val painter = rememberImagePainter(
                data = uri,
                builder = {
                placeholder(R.drawable.yara) <-- the placeholder image blinks quickly when app is loaded and then disappear...
     }
  )
    Image(
       painter = painter,
       contentDescription = "User Image",
       modifier = Modifier.size(imageSize) )
 }
}

是 Uri 选择器或图像显示的问题吗?

房间数据库中的其他内容正确显示(字符串)

我尝试在模拟器和真实设备上使用代码,这两种情况都是相同的......

我刚刚注意到的一件事...如果我使用图像选择器并选择以前选择的图像(它的 uri 在数据库中),图像会自动显示在应用程序中的其他位置,而不需要执行任何其他操作...只需选择图像并显示它甚至不保存...

android uri android-jetpack-compose
2个回答
2
投票

嗯,我不知道该怎么做

takePersistableUriPermission()
,我还在学习。但我创建了一个转换器类(最长的解决方案)来使用 Uri 将其转换为位图...但我必须首先使其可绘制。我不知道如何直接从 Uri 中获取位图。

我首先更新了数据类

@Entity
data class Contact(
    @PrimaryKey()
    val id: UUID = UUID.randomUUID(),
    val name: String,
    val land_line : String,
    val mobile: String,
    val contact_photo: Bitmap
)

为数据库中的位图添加了

TypeConverter

    @TypeConverter
    fun fromBitmap(bitmap: Bitmap): ByteArray {
        val outputStream = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
        return outputStream.toByteArray()
    }

    @TypeConverter
    fun toBitmap(byteArray: ByteArray): Bitmap {
        return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
    }

将值类型一直更改为 UI,并在 Util 包中创建一个转换器类,将 Uri 转换为可绘制然后位图(我知道这看起来很愚蠢 - 任何更好的解决方案都可以接受)。

suspend fun convertToBitmap(uri: Uri, context: Context, widthPixels: Int, heightPixels: Int): Bitmap? {
    val mutableBitmap = Bitmap.createBitmap(widthPixels, heightPixels, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(mutableBitmap)
    val drawable = getDrawable(uri, context)
    drawable?.setBounds(0, 0, widthPixels, heightPixels)
    drawable?.draw(canvas)
    return mutableBitmap
}


@OptIn(ExperimentalCoilApi::class)
suspend fun getDrawable(uri: Uri, context: Context): Drawable? {
    val imageLoader = ImageLoader.Builder(context)
        .availableMemoryPercentage(0.25)
        .allowHardware(false)
        .crossfade(true)
        .build()

    val request= ImageRequest.Builder(context)
        .data(uri)
        .build()
    return imageLoader.execute(request).drawable
}

它现在正在工作并且加载完美。我知道我的宽度和高度为 50 50 px,但无论如何这是一个测试。


0
投票

我一直在使用 Compose,并且对此有一个解决方案,所以让我为您提供一些背景信息。

首先,我有一个带有 CRUD 事务的笔记应用程序,我使用了 room 和匕首柄。 因此,在我的可组合 AddNoteScreeen.kt 中,我添加或更新注释,但这是重要的部分

val mediaPickerLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.PickVisualMedia(),
        onResult = { uri ->
            if (uri != null) {
                val flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
                context.contentResolver.takePersistableUriPermission(uri, flag)
                noteViewModel.onImageChanged(uri)
            }

        }
    )

我在上面声明了这个可组合组件,这是第一个可组合组件。这段代码对 Android 说你要创建一个启动器并等待结果,合同部分说你要使用一个标准活动,即 PickVisualMedia(),并且在该活动上,你只能选择一个元素。我鼓励您查看此处的文档:照片选择器

然后,我检查我的结果,如果它不为空,我将其分配给我的 ViewModel 中的状态。但在我声明一个标志之前,该标志允许您在后台上传大文件,您可能需要此访问权限持续较长时间,并且它必须与您选择的 URI 相关联。

最后,您可以使用下一个代码启动它

 Button(onClick = {
                    mediaPickerLauncher.launch(
                        PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
                    )
                }) {
                    Text(text = "Select Image")
                }

PickVisualMediaRequest 创建启动该 Activity 的请求,在此部分中,您可以声明需要显示哪种资源,例如视频、图像或 GIF。

在这个过程中,我使用 Room 保存图像,显然,这个响应集中在 UI 以及如何保存元素上。

这里是 GitHub 存储库

© www.soinside.com 2019 - 2024. All rights reserved.