You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose中通过navController传递相册图片Uri至目标页面

解决方案

1. 新增ViewModel共享选中的Uri状态

通过ViewModel实现MainActivity(或Compose组件)与导航逻辑的状态共享,存储选中的图片Uri:

class MainViewModel : ViewModel() {
    val selectedImageUri = MutableStateFlow<String?>(null)

    fun setSelectedImageUri(uri: String) {
        selectedImageUri.value = uri
    }
}

2. 调整MainActivity代码

将选中的Uri编码后存入ViewModel,同时初始化ViewModel并传递给导航组件:

class MainActivity : ComponentActivity() {
    private lateinit var viewModel: MainViewModel

    @ExperimentalPermissionsApi
    @ExperimentalCoroutinesApi
    @ExperimentalCoilApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel = ViewModelProvider(this)[MainViewModel::class.java]
        setContent {
            val navController = rememberNavController()
            AppNavigation(navController = navController, viewModel = viewModel)
        }
    }
}

3. 重构主页面的相册选择与导航逻辑

将相册选择器移到Composable内部(更符合Jetpack Compose最佳实践),并监听ViewModel的Uri状态触发导航:

fun firstScreen(navController: NavHostController, viewModel: MainViewModel) {
    // 监听选中的Uri状态
    val selectedUri by viewModel.selectedImageUri.collectAsStateWithLifecycle()

    // 当Uri不为空时导航到SecondScreen
    LaunchedEffect(selectedUri) {
        selectedUri?.let {
            navController.navigate(AppScreens.SecondScreenImage.route + "/$it")
            // 导航后重置状态,避免重复触发导航
            viewModel.setSelectedImageUri("")
        }
    }

    // 相册按钮区域代码
    Column {
        Box(
            Modifier
                .weight(1f)
                .clickable {
                    // 在Compose中创建相册选择器
                    val pickMediaLauncher = rememberLauncherForActivityResult(
                        ActivityResultContracts.PickVisualMedia()
                    ) { uri ->
                        uri?.let {
                            // 编码Uri,处理特殊字符避免导航参数错误
                            val encodedUri = Uri.encode(it.toString().replace('%', '|'))
                            viewModel.setSelectedImageUri(encodedUri)
                        }
                    }
                    pickMediaLauncher.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))
                },
            contentAlignment = Alignment.Center
        ) {
            Column(horizontalAlignment = Alignment.CenterHorizontally) {
                val gallery = painterResource(id = R.drawable.icons8_im_genes_de_carpetas_32__1_)
                Image(
                    painter = gallery,
                    contentDescription = "打开相册",
                    modifier = Modifier.size(36.dp, 36.dp)
                )
                Text(text = "Imágenes", fontFamily = Inter, fontSize = 12.sp)
            }
        }
    }
}

4. 修正SecondScreen的MLKit调用逻辑

不能在Composable根作用域直接执行MLKit识别操作,需用LaunchedEffect包裹,确保只在Uri变化时执行一次:

@Composable
fun secondScreen(navController: NavHostController, textUri: String?) {
    val scaffoldState = rememberScaffoldState()
    val context = LocalContext.current
    val textChanged = remember { mutableStateOf("") }
    val recognizer = remember { TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS) }

    // 解码Uri,恢复原始格式
    val imageUri = textUri?.let {
        val decodedUri = Uri.decode(it.replace('|', '%'))
        Uri.parse(decodedUri)
    }

    // 执行MLKit文字识别
    LaunchedEffect(imageUri) {
        imageUri?.let { uri ->
            try {
                val image = InputImage.fromFilePath(context, uri)
                val result = recognizer.process(image).await()
                textChanged.value = result.text
            } catch (e: Exception) {
                Log.e("TEXT_REC", e.message.toString())
            }
        }
    }

    // 页面UI展示
    Scaffold(scaffoldState = scaffoldState) { padding ->
        Column(modifier = Modifier.padding(padding)) {
            imageUri?.let {
                AsyncImage(
                    model = it,
                    contentDescription = "选中的图片",
                    modifier = Modifier.fillMaxWidth().height(200.dp)
                )
            }
            Text(text = textChanged.value, modifier = Modifier.padding(16.dp))
        }
    }
}

5. 确认导航逻辑有效性

你的原有导航配置已正确设置SecondScreenImage的路由参数,保持不变即可:

fun AppNavigation(navController: NavHostController, viewModel: MainViewModel) {
    NavHost(navController = navController, startDestination = AppScreens.SplashScreen.route) {
        composable(route = AppScreens.SplashScreen.route) {
            splashScreen(navController)
        }
        composable(route = AppScreens.FirstScreen.route) {
            firstScreen(navController, viewModel)
        }
        composable(route = AppScreens.Camera.route) {
            CameraCapture(navController = navController)
        }
        composable(
            route = AppScreens.SecondScreenImage.route + "/{textUri}",
            arguments = listOf(navArgument("textUri") { type = NavType.StringType })
        ) { backStackEntry ->
            secondScreen(
                navController = navController,
                textUri = backStackEntry.arguments?.getString("textUri")
            )
        }
        composable(
            route = AppScreens.SecondScreenText.route + "/{text}",
            arguments = listOf(navArgument("text") { type = NavType.StringType })
        ) { backStackEntry ->
            secondScreenText(
                navController = navController,
                text = backStackEntry.arguments?.getString("text")
            )
        }
    }
}

关键说明

  • 状态共享:通过ViewModel的StateFlow实现跨组件的状态同步,避免局部变量无法访问的问题。
  • Uri编码解码:处理Uri中的特殊字符,确保导航参数传递时不会出现解析错误。
  • 生命周期安全:MLKit操作放在LaunchedEffect中,确保符合Compose的重组机制,避免重复执行。
  • 架构优化:将相册选择器移到Composable内部,避免跨层传递Launcher对象,代码更内聚。

内容的提问来源于stack exchange,提问作者Eduardo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 07:45:00