Android跨Fragment传递并显示CameraX拍摄图片的方法
Android 跨Fragment传递并显示CameraX拍摄图片实现方案
方案选型说明
- 绝对不要传递Bitmap:Bitmap属于大内存对象,通过Bundle跨组件传递很容易触发
TransactionTooLargeException崩溃,还会平白占用多倍不必要的内存,属于吃力不讨好的方案。 - 最优方案是直接传递Uri:Uri本身实现了Parcelable接口,天然支持Navigation的Bundle、Safe Args传递,你之前遇到传值为null的问题完全是代码逻辑错误导致的,不是方案本身不可行。
原有代码问题排查
- 传参逻辑错误:你没有直接传递
output.savedUri这个Uri对象,反而取了Uri的path字段转成字符串传递。Android 10+开启分区存储后,MediaStore返回的都是content://类型的Uri,靠截取的文件路径根本无法正常访问文件,从根源上会导致加载失败。 - 取参逻辑错误:你传参时是直接把值存在Bundle的对应key下,取值时却先调用
arguments?.getBundle("image")试图获取一个子Bundle,类型完全不匹配,拿到的结果自然是null。
修正后可运行代码
1. 修改CameraFragment的拍照回调传参逻辑
把原来onImageSaved方法里的传参部分替换为以下代码:
override fun onImageSaved(output: ImageCapture.OutputFileResults) { val savedUri = output.savedUri ?: return val msg = "Photo capture succeeded: $savedUri" Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show() Timber.d(msg) // 直接把Uri作为Parcelable存入Bundle,不要转path字符串 val bundle = bundleOf("captured_image_uri" to savedUri) Navigation.findNavController(requireView()).navigate( R.id.cameraFragmentToCameraFinalFragment, bundle ) }
如果你用Navigation Safe Args,直接在导航图的argument配置里把参数类型设为
android.net.Uri,传参时直接传入Uri即可,不需要手动构造Bundle。
2. 修改PhotoFinalFragment的参数读取与图片加载逻辑
把原来onCreateView里的参数读取、图片加载部分替换为以下代码:
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentPhotoFinalBinding.inflate(inflater, container, false) // 直接从arguments根节点取Parcelable类型的Uri,不需要取子Bundle val imageUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { arguments?.getParcelable("captured_image_uri", Uri::class.java) } else { @Suppress("DEPRECATION") arguments?.getParcelable("captured_image_uri") } imageUri?.let { uri -> // 注意:原生setImageURI会在主线程解码图片,大图容易卡顿ANR,建议替换为Glide/Coil等图片加载库 // Coil加载示例:binding.imageView.load(uri) // Glide加载示例:Glide.with(this).load(uri).into(binding.imageView) binding.imageView.setImageURI(uri) } binding.btnYes.setOnClickListener { Navigation.findNavController(requireView()).navigate(R.id.cameraFinalFragmentToStoryFragment) } binding.btnNo.setOnClickListener { Navigation.findNavController(requireView()).navigate(R.id.cameraFinalFragmentTocameraFragment) } return binding.root }
额外注意事项
- 不要直接拿
content://类型Uri的path字段当本地文件路径使用,这个写法在开启分区存储的设备上完全没有权限读取对应文件,必须通过ContentResolver获取文件描述符访问,或者直接把Uri交给图片加载库处理。 - 大尺寸拍摄图不建议直接用
ImageView.setImageURI()加载,这个方法会在主线程执行图片解码,很容易造成界面卡顿甚至ANR,成熟的图片加载库会自动处理异步解码、内存缓存、尺寸压缩,体验稳定很多。
内容的提问来源于stack exchange,提问作者oyeraghib
相关产品推荐
相关产品推荐

