剪贴板
源:Android ClipboardManager | iOS UIPasteboard
剪贴板操作是应用间数据交换的重要方式。本文将展示如何跨平台实现文本、图片等内容的复制与粘贴。
平台差异对比
| 平台 | 原生 API | 支持类型 | 权限要求 | 特性 |
|---|---|---|---|---|
| Android | ClipboardManager | 文本、URI、Intent | 无需权限 | 剪贴板监听、多类型数据 |
| iOS | UIPasteboard | 文本、图片、URL | 无需权限 | 过期时间、跨设备同步 |
| Desktop | Toolkit.getDefaultToolkit() | 文本、图片 | 无需权限 | 系统剪贴板 |
expect/actual 实现方案
API 核心签名说明
expect object ClipboardManagerfun ClipboardManager.copyText(text: String)fun ClipboardManager.getText(): String?fun ClipboardManager.hasText(): Booleanfun ClipboardManager.clear()
标准代码块
kotlin
expect object ClipboardManager {
fun copyText(text: String)
fun getText(): String?
fun hasText(): Boolean
fun clear()
}
// 业务层使用
class ShareHelper {
fun copyToClipboard(text: String) {
ClipboardManager.copyText(text)
// 可选:显示 Toast 提示
println("已复制到剪贴板")
}
fun pasteFromClipboard(): String? {
return if (ClipboardManager.hasText()) {
ClipboardManager.getText()
} else {
null
}
}
fun shareInviteCode(code: String) {
copyToClipboard("邀请码:$code")
}
}kotlin
import android.content.ClipData
import android.content.ClipboardManager as AndroidClipboardManager
import android.content.Context
actual object ClipboardManager {
private lateinit var clipboardManager: AndroidClipboardManager
fun init(context: Context) {
clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE)
as AndroidClipboardManager
}
actual fun copyText(text: String) {
val clip = ClipData.newPlainText("text", text)
clipboardManager.setPrimaryClip(clip)
}
actual fun getText(): String? {
if (!clipboardManager.hasPrimaryClip()) {
return null
}
val clip = clipboardManager.primaryClip ?: return null
if (clip.itemCount == 0) {
return null
}
return clip.getItemAt(0)?.text?.toString()
}
actual fun hasText(): Boolean {
return clipboardManager.hasPrimaryClip() &&
clipboardManager.primaryClipDescription?.hasMimeType("text/plain") == true
}
actual fun clear() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
clipboardManager.clearPrimaryClip()
} else {
// Android 8 及以下无法清空剪贴板
val clip = ClipData.newPlainText("", "")
clipboardManager.setPrimaryClip(clip)
}
}
}kotlin
import platform.UIKit.UIPasteboard
actual object ClipboardManager {
private val pasteboard = UIPasteboard.generalPasteboard
actual fun copyText(text: String) {
pasteboard.string = text
}
actual fun getText(): String? {
return pasteboard.string
}
actual fun hasText(): Boolean {
return pasteboard.string != null
}
actual fun clear() {
pasteboard.items = emptyList()
}
}kotlin
import java.awt.Toolkit
import java.awt.datatransfer.DataFlavor
import java.awt.datatransfer.StringSelection
actual object ClipboardManager {
private val clipboard = Toolkit.getDefaultToolkit().systemClipboard
actual fun copyText(text: String) {
val selection = StringSelection(text)
clipboard.setContents(selection, null)
}
actual fun getText(): String? {
return try {
if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
clipboard.getData(DataFlavor.stringFlavor) as? String
} else {
null
}
} catch (e: Exception) {
null
}
}
actual fun hasText(): Boolean {
return clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)
}
actual fun clear() {
clipboard.setContents(StringSelection(""), null)
}
}代码封装示例
以下是带自动清空和提示的完整封装:
kotlin
// commonMain
class SecureClipboard(private val manager: ClipboardManager) {
private var clearJob: Job? = null
fun copyWithAutoClear(
text: String,
clearAfterMillis: Long = 60_000L,
onCopied: () -> Unit = {}
) {
manager.copyText(text)
onCopied()
// 取消之前的清空任务
clearJob?.cancel()
// 定时清空
clearJob = CoroutineScope(Dispatchers.Default).launch {
delay(clearAfterMillis)
manager.clear()
}
}
fun cancelAutoClear() {
clearJob?.cancel()
clearJob = null
}
}依赖补充
无需额外依赖
剪贴板功能仅依赖平台标准库,无需添加第三方依赖。
Android 初始化
需要在应用启动时初始化:
kotlin
// androidMain
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
ClipboardManager.init(this)
}
}实战坑点
Android 后台访问限制
Android 10+ 限制
Android 10+ 后台应用访问剪贴板会返回空内容。
解决方案:
只在应用前台时读取剪贴板,或使用 ContentProvider 共享数据。
iOS 剪贴板提示
iOS 14+ 隐私提示
iOS 14+ 首次访问剪贴板会在顶部显示"粘贴自..."提示。
最佳实践:
kotlin
// 仅在用户点击"粘贴"按钮时读取
fun onPasteButtonClick() {
val text = ClipboardManager.getText()
// 使用文本
}剪贴板数据类型
::: caution 非文本数据 本示例仅处理文本,图片、文件等需要额外处理。 :::
图片复制示例(Android):
kotlin
// androidMain
fun copyImage(imageUri: Uri) {
val clip = ClipData.newUri(contentResolver, "image", imageUri)
clipboardManager.setPrimaryClip(clip)
}Desktop 线程安全
AWT 线程
Desktop 剪贴板操作应在 EDT(Event Dispatch Thread)执行。
解决方案:
kotlin
// jvmMain
import javax.swing.SwingUtilities
fun copyTextSafe(text: String) {
SwingUtilities.invokeLater {
clipboard.setContents(StringSelection(text), null)
}
}