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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 06:01:04