【问题标题】:Image picker from gallery for Jetpack compose - Android/KotlinJetpack compose 库中的图像选择器 - Android/Kotlin
【发布时间】:2021-08-20 03:36:52
【问题描述】:

我想使用 Jetpack compose 实现一个图像选择器 我正在寻找解决方案,我发现了一些类似的教程 https://ngengesenior.medium.com/pick-image-from-gallery-in-jetpack-compose-5fa0d0a8ddaf 我使用了他们解释的代码,它运行良好,但我有一个问题!

我的应用程序包含一个活动“MainActivity”,它开始呈现组合组件,我的一个屏幕是一个表单,其中包含一个用于选择图像的字段和其他字段,当我使用下面的代码时,它会打开图库并选择一个图像当点击 OK 时,它会转到 MainActivity,但我需要留在表单的同一屏幕上,以便用户可以继续填写表单,我将列出代码,希望有人可以帮助我

val launcher = rememberLauncherForActivityResult(contract =
    ActivityResultContracts.GetContent()) { uri: Uri? ->
        imageUri = uri
    }
    Column() {
        Button(onClick = {
            launcher.launch("image/*")
        }) {
            Text(text = "Pick image")
        }

        Spacer(modifier = Modifier.height(12.dp))

        imageUri?.let {
            if (Build.VERSION.SDK_INT < 28) {
                bitmap.value = MediaStore.Images
                    .Media.getBitmap(context.contentResolver,it)

            } else {
                val source = ImageDecoder
                    .createSource(context.contentResolver,it)
                bitmap.value = ImageDecoder.decodeBitmap(source)
            }

            bitmap.value?.let {  btm ->
                Image(bitmap = btm.asImageBitmap(),
                    contentDescription =null,
                    modifier = Modifier.size(400.dp))
            }
        }

    }
  • 我的 MainAcrivity,我有很多嵌套屏幕,但假设我在主屏幕上有一个按钮,可以转到如下所示的窗体屏幕
class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            Button(
                onClick = {
                    navController.navigate(AppScreens.FormScreen.route)
                },
                ) {
                Text(text = "Go to form screen" )
            }
        }
    }
}
  • 这是我的表单屏幕,包括许多字段,例如文本、数字、日期和图像字段
@Composable
fun FormScreen() {
    var imageUri by remember { mutableStateOf<Uri?>(null) }
    val context = LocalContext.current
    var bitmap by remember { mutableStateOf<Bitmap?>(null) }

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

    Column {
        // some text field in the form
        // another number field in the form
        // select image filed in the form
        CustomInputFieldContainer(
            label = "select image"
        ) {
            Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
                Column(horizontalAlignment = Alignment.CenterHorizontally) {

                    imageUri?.let {
                        if (Build.VERSION.SDK_INT < 28) {
                            bitmap = MediaStore.Images
                                .Media.getBitmap(context.contentResolver,it)

                        } else {
                            val source = ImageDecoder
                                .createSource(context.contentResolver,it)
                            bitmap = ImageDecoder.decodeBitmap(source)
                        }

                        bitmap.let {  btm ->
                            Image(bitmap = btm.asImageBitmap(),
                                contentDescription =null,
                                modifier = Modifier.size(400.dp))
                        }

                    }

                    Button(
                        onClick = { launcher.launch("image/*") },
                        contentPadding = PaddingValues(),
                        modifier = Modifier.background(Color.Yellow)
                    ) {
                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .wrapContentSize(Alignment.BottomCenter)
                                .padding(vertical = 10.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Icon(imageVector = Icons.Filled.AddAPhoto, contentDescription = null)
                            Spacer(modifier = Modifier.width(8.dp))
                            Text(text = "Add Photo")
                        }
                    }
                }
            }
        }
    }


}

现在我想在单击选择图像按钮时从图库中选择一张图片,然后返回我的表单以完成其余字段,当我尝试上述代码时,我可以选择一张图片,但它进入了主要活动这让我丢失了数据

对解决这个问题有什么帮助吗?

【问题讨论】:

    标签: android kotlin android-jetpack-compose


    【解决方案1】:

    当您说“它工作正常”时,您的意思是您检查了“val imageUri'”是否选择了正确的文件 Uri?

    从 Image-Picker 中选择图像后,您是否从 logcat 中收到任何错误?

    试试这个,如果它有任何作用,请告诉我们

    var imageUri = remember { mutableStateOf<Uri?>(null) } // UPDATE
    val context = LocalContext.current
    var bitmap by remember { mutableStateOf<Bitmap?>(null) }
    
    val launcher = rememberLauncherForActivityResult(contract =
    ActivityResultContracts.GetContent()) { uri: Uri? -> 
        imageUri.value = uri // UPDATE
    }
    

    【讨论】:

    • 问题是在选择图像后它会进入主活动而不是留在表单屏幕中!我刚刚对其进行了测试并给了我正确的 Uri,甚至它只是显示了图像但它导航到了主要活动,所以我需要从一开始就打开表单屏幕,我需要在选择后将其保持在同一屏幕上的东西图片,你明白我的问题吗?
    • 我明白了,我们可以看看你的 navController 是如何设置的吗?
    • 这只是文档中描述的基本内容,您可以在以下位置看到主要活动和主导航中的一些代码:gist.github.com/mstva/a391b2e1c57a2e02d8cbeecd3c2d3e9b 如果您想了解其他信息,请告诉我
    【解决方案2】:

    确实,这似乎与您使用启动器的方式无关。 我知道问题是在您从图像选择器中选择图像文件后屏幕返回到主要活动,此时我将检查 backStackEntry 的管理方式。 尝试从 mainActivity 中注释掉“compose”主体并观察它是否有任何作用。 ---> 注释掉这个块。

    {backStackEntry -> backStackEntry.arguments?.getString("form)?.let { _form ->
                val form = Gson().fromJson(_form, FormModel::class.java)
                FormDetailScreen(
                    navController = navController,
                    form = form,
                    formViewModel = formViewModel
                )
            }
        }
    

    【讨论】:

      【解决方案3】:

      如果我能很好地理解你的问题,我的情况和你一样。 这就是我的做法。这有点hacky,但它有效。

      我有这个

      //The clickable composable
          ProfilePictureView(
                          avatarUrl, firstname, lastname, isUpdatingProfileImage,
                          Modifier
                              .padding(top = 50.dp)
                              .size(110.dp)
                              .align(Alignment.CenterHorizontally),
                      onClick = {
                              scope.launch {
                                  openImagePicker(permissionManager, LocalContext.current as MainActivity)
                              }
                      }) 
      
      
      
      fun openImagePicker(permissionManager: PermissionManager, activity: AppCompatActivity) {
          permissionManager.requestStoragePermission {
              if (it) {
                  val intent = Intent(Intent.ACTION_PICK)
                  intent.type = "image/*"
                  startActivityForResult(activity, intent, MainActivity.IMAGE_PICK_CODE, null)
              }
          }
      }
      

      在应用中处理权限的类

      class PermissionManager(private val activity: AppCompatActivity) {
      
          private val errorHandler = CoroutineExceptionHandler { _, throwable ->
              Timber.e("$throwable")
          }
      
          private val scope = CoroutineScope(Job() + Dispatchers.IO + errorHandler)
      
          fun requestStoragePermission(onResult: (Boolean) -> Unit) {
              requestPermission(
                  Manifest.permission.READ_EXTERNAL_STORAGE,
                  Permissions.STORAGE.code,
                  onResult
              )
          }
      
          fun requestPermission(permission: String, permissionCode: Int, onResult: (Boolean) -> Unit) {
              if (
                  ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_DENIED
              ) {
                  val permissions = arrayOf(permission)
      
                  scope.launch {
                      activity.requestPermissions(permissions, permissionCode)
      
                      LiveEventBus.listen<EventPermissionResult>().collect {
                          onResult.invoke(it.isGranted)
                      }
                  }
              } else {
                  onResult.invoke(true)
              }
          }
      
      
          enum class Permissions(val code: Int) {
              STORAGE(1001)
          }
      }
      

      然后在MainActivity中我们拿到图片后,用eventBus触发调用

      class MainActivity : AppCompatActivity() {
          override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
          super.onActivityResult(requestCode, resultCode, data)
      
          if (resultCode == RESULT_OK) {
              lifecycleScope.launch {
                  when (requestCode) {
                      IMAGE_PICK_CODE -> data?.data?.let {
                          LiveEventBus.send(
                              EventImageSelected(it)
                          )
                      }
                  }
              }
          }
        }
      }
      

      可能有更好的解决方案

      可能值得检查 Jetpack Compose Accompanist 库 目前仍处于试验阶段 https://github.com/google/accompanist/tree/main/permissions 和这里的文档: https://google.github.io/accompanist/permissions/

      【讨论】:

        猜你喜欢
        • 2023-01-20
        • 1970-01-01
        • 1970-01-01
        • 2021-08-08
        • 1970-01-01
        • 1970-01-01
        • 2018-11-02
        • 2021-01-18
        • 2022-09-29
        相关资源
        最近更新 更多