如何构建支持Android和iOS的KMM图片处理库?
KMM跨平台图片处理库实现思路修正与方案
核心思路验证
你的基础方向是对的,但有个关键问题:Common模块不能直接返回平台专属的Image类型(Android的android.graphics.Image或iOS的UIImage),因为KMM的Common层是无平台依赖的,必须用expect/actual机制抽象图片类型,才能在各平台实现具体逻辑。
正确实现方案
1. Common模块:抽象核心接口与预期类
在Common层定义预期的图片类型,以及核心图片操作的抽象方法,实现跨平台逻辑的统一入口:
// Common/src/commonMain/kotlin/com/yourpackage/image/ImageProcessor.kt expect class PlatformImage interface ImageProcessor { // 加载本地资源图片 fun loadLocalImageResource(resourceId: String): PlatformImage // 创建空白新图片 fun createNewImage(width: Int, height: Int): PlatformImage // 图片处理:添加文字示例 fun addTextToImage(image: PlatformImage, text: String, x: Int, y: Int): PlatformImage // 图片处理:绘制线条示例 fun drawLineOnImage(image: PlatformImage, startX: Int, startY: Int, endX: Int, endY: Int): PlatformImage } // Common层提供统一入口 expect fun getImageProcessor(): ImageProcessor
2. Android模块:实现具体逻辑
在Android层用Bitmap映射PlatformImage,并基于Android原生API完成图片操作:
// Android/src/androidMain/kotlin/com/yourpackage/image/ImageProcessorAndroid.kt actual typealias PlatformImage = android.graphics.Bitmap actual fun getImageProcessor(): ImageProcessor = ImageProcessorAndroid() class ImageProcessorAndroid : ImageProcessor { override fun loadLocalImageResource(resourceId: String): PlatformImage { val context = ApplicationProvider.getApplicationContext<Context>() val resId = context.resources.getIdentifier(resourceId, "drawable", context.packageName) return BitmapFactory.decodeResource(context.resources, resId) } override fun createNewImage(width: Int, height: Int): PlatformImage { return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) } override fun addTextToImage(image: PlatformImage, text: String, x: Int, y: Int): PlatformImage { val mutableBitmap = image.copy(image.config, true) val canvas = Canvas(mutableBitmap) val paint = Paint().apply { color = Color.BLACK textSize = 48f isAntiAlias = true } canvas.drawText(text, x.toFloat(), y.toFloat(), paint) return mutableBitmap } override fun drawLineOnImage(image: PlatformImage, startX: Int, startY: Int, endX: Int, endY: Int): PlatformImage { val mutableBitmap = image.copy(image.config, true) val canvas = Canvas(mutableBitmap) val paint = Paint().apply { color = Color.RED strokeWidth = 5f isAntiAlias = true } canvas.drawLine(startX.toFloat(), startY.toFloat(), endX.toFloat(), endY.toFloat(), paint) return mutableBitmap } }
3. iOS模块:实现具体逻辑
在iOS层用UIImage映射PlatformImage,基于iOS原生CoreGraphics完成图片操作:
// iOS/src/iosMain/kotlin/com/yourpackage/image/ImageProcessorIos.kt actual typealias PlatformImage = UIImage actual fun getImageProcessor(): ImageProcessor = ImageProcessorIos() class ImageProcessorIos : ImageProcessor { override fun loadLocalImageResource(resourceId: String): PlatformImage { return UIImage.imageNamed(resourceId) ?: UIImage() } override fun createNewImage(width: Int, height: Int): PlatformImage { UIGraphicsBeginImageContextWithOptions(CGSize(width.toDouble(), height.toDouble()), false, 0.0) val image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image ?: UIImage() } override fun addTextToImage(image: PlatformImage, text: String, x: Int, y: Int): PlatformImage { UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale) image.drawInRect(CGRect(x: 0.0, y: 0.0, width: image.size.width, height: image.size.height)) val textAttributes = mutableMapOf<NSAttributedStringKey, Any>( NSAttributedStringKey.foregroundColor to UIColor.blackColor, NSAttributedStringKey.font to UIFont.systemFontOfSize(48.0) ) text.drawAtPoint(CGPoint(x.toDouble(), y.toDouble()), withAttributes = textAttributes) val newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage ?: image } override fun drawLineOnImage(image: PlatformImage, startX: Int, startY: Int, endX: Int, endY: Int): PlatformImage { UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale) image.drawInRect(CGRect(x: 0.0, y: 0.0, width: image.size.width, height: image.size.height)) val context = UIGraphicsGetCurrentContext() context?.setStrokeColor(UIColor.redColor.CGColor) context?.setLineWidth(5.0) context?.moveTo(CGPoint(startX.toDouble(), startY.toDouble())) context?.addLineTo(CGPoint(endX.toDouble(), endY.toDouble())) context?.strokePath() val newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage ?: image } }
平台端使用方式
- Android端:
val processor = getImageProcessor() val bitmap = processor.loadLocalImageResource("ic_launcher") val editedBitmap = processor.addTextToImage(bitmap, "Hello KMM", 50, 50) imageView.setImageBitmap(editedBitmap)
- iOS端:
let processor = getImageProcessor() let image = processor.loadLocalImageResource(resourceId: "ic_launcher") let editedImage = processor.addTextToImage(image: image, text: "Hello KMM", x: 50, y: 50) imageView.image = editedImage
关键注意点
- Common层绝对不能直接引用平台专属API,所有平台相关逻辑必须通过expect/actual隔离
- 复杂图片处理逻辑(如滤镜、裁剪)可在Common层定义抽象方法,平台层用各自原生API实现
- 若需跨平台传递图片二进制数据,可在Common层用
ByteArray作为中间载体,各平台自行转换
内容的提问来源于stack exchange,提问作者Pieter
相关产品推荐
相关产品推荐

