Android 6-10设备视图转图片并多平台分享的实现建议
Android 6-10 视图转图片及多平台分享实现方案
适配关键要点
- 视图转Bitmap:弃用已标记为废弃的
DrawingCache,改用Canvas绘制方式,避免截图不全、空指针等问题 - 存储适配:Android 10启用Scoped Storage,通过
MediaStore将图片写入公共图库,确保其他应用可访问;Android 6-9沿用文件存储但需动态申请权限 - 权限处理:Android 6-9需申请
WRITE_EXTERNAL_STORAGE权限,Android 10及以上无需该权限即可通过MediaStore保存 - 定向分享:通过Intent实现WhatsApp、Skype及邮件的直接分享,或提供选择器让用户挑选应用
完整代码实现
1. 权限声明(AndroidManifest.xml)
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
2. Kotlin 核心代码
private const val REQUEST_STORAGE_PERMISSION = 1001 // 将指定View转换为Bitmap private fun viewToBitmap(view: View): Bitmap { val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888) val canvas = Canvas(bitmap) view.draw(canvas) return bitmap } // 保存Bitmap到设备存储 private fun saveBitmapToStorage(bitmap: Bitmap): Uri? { val imageName = "ServiceInvoice_${businessName}_${SimpleDateFormat("dd-MM-yyyy hh:mm:ss", Locale.getDefault()).format(System.currentTimeMillis())}" return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10+ 使用MediaStore val contentValues = ContentValues().apply { put(MediaStore.Images.Media.DISPLAY_NAME, imageName) put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) } contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)?.let { uri -> contentResolver.openOutputStream(uri)?.use { outputStream -> bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) } uri } } else { // Android 6-9 使用文件存储 val picturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) val imageFile = File(picturesDir, "$imageName.jpg") try { FileOutputStream(imageFile).use { outputStream -> bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) } // 通知图库更新 MediaScannerConnection.scanFile(this, arrayOf(imageFile.path), null, null) Uri.fromFile(imageFile) } catch (e: Exception) { Log.e("STORAGE", "Save failed: ${e.message}") null } } } // 分享图片到指定应用 private fun shareImage(uri: Uri) { // 通用分享Intent(显示所有支持图片分享的应用) val shareIntent = Intent(Intent.ACTION_SEND).apply { type = "image/jpeg" putExtra(Intent.EXTRA_STREAM, uri) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } // 定向分享到指定应用(包名可根据实际情况调整) val appTargets = listOf( "com.whatsapp" to "WhatsApp", "com.skype.raider" to "Skype", "com.google.android.gm" to "Gmail" ) val targetIntents = mutableListOf<Intent>() appTargets.forEach { (packageName, appName) -> packageManager.getLaunchIntentForPackage(packageName)?.let { Intent(shareIntent).apply { setPackage(packageName) putExtra(Intent.EXTRA_TITLE, "分享发票") targetIntents.add(this) } } } if (targetIntents.isNotEmpty()) { // 创建选择器显示指定应用 val chooserIntent = Intent.createChooser(targetIntents.removeAt(0), "选择分享应用") chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toTypedArray()) startActivity(chooserIntent) } else { // 没有指定应用时显示通用选择器 startActivity(Intent.createChooser(shareIntent, "分享发票")) } } // 主入口方法:生成并分享发票 private fun shareInvoice() { // 确保视图已完成布局 binding.consLayout1.post { val bitmap = viewToBitmap(binding.consLayout1) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { // Android 6-9 检查存储权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_STORAGE_PERMISSION) return@post } } val imageUri = saveBitmapToStorage(bitmap) if (imageUri != null) { showMessage("发票生成成功") shareImage(imageUri) } else { showMessage("发票生成失败") } } } // 权限申请回调 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_STORAGE_PERMISSION) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { shareInvoice() } else { showMessage("需要存储权限才能生成发票") } } }
关键细节说明
- 视图转Bitmap:通过
View.draw(Canvas)直接绘制视图内容,比DrawingCache更稳定,避免因视图未完全渲染导致的截图问题 - Scoped Storage适配:Android 10+使用
MediaStore插入图片,无需直接操作文件路径,符合系统权限规范 - 权限处理:仅在Android 6-9版本申请存储权限,Android 10+无需该权限即可完成保存
- 分享权限:添加
FLAG_GRANT_READ_URI_PERMISSION确保接收应用能读取图片Uri - 定向分享:通过指定应用包名过滤分享选项,若目标应用未安装则自动回退到通用选择器
内容的提问来源于stack exchange,提问作者Yug Mewada
相关产品推荐
相关产品推荐

