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

如何构建支持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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 04:25:21