本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

   在鸿蒙(HarmonyOS)中实现复制粘贴功能需使用@kit.BasicServicesKit的剪贴板服务:

一、约束限制

  1. 容量限制

    • 剪贴板内容总大小默认为128MB

    • PC/二合一设备可通过系统配置调整上限,范围为128MB至2GB

  2. 操作限制

    • 为保证数据准确性,同一时间只能支持一个复制操作

  3. 权限管控

    • 从API version 12开始,剪贴板读取接口增加了权限管控,以提升用户隐私安全保护

权限要求(API version 12+)

  • 读取剪贴板需申请权限:ohos.permission.READ_PASTEBOARD
  • module.json5中添加:
"requestPermissions": [{
  "name": "ohos.permission.READ_PASTEBOARD"
}]

二、支持的数据类型

剪贴板支持两种方式处理数据:

(一)基础数据类型

支持以下五种基础数据类型:

  • 文本(text/plain)

  • HTML(text/html)

  • URI(text/uri)

  • Want(text/want)

  • PixelMap(pixelMap)

ArkTS与NDK接口数据类型对应关系:
ArkTS数据类型 NDK数据类型
MIMETYPE_PIXELMAP UDMF_META_OPENHARMONY_PIXEL_MAP
MIMETYPE_TEXT_HTML UDMF_META_HTML
MIMETYPE_TEXT_PLAIN UDMF_META_PLAIN_TEXT
MIMETYPE_TEXT_URI UDMF_META_GENERAL_FILE_URI
MIMETYPE_TEXT_WANT NDK接口不支持

(二)统一数据类型

为了方便应用间数据交互,减少数据类型适配工作量,剪贴板还支持统一数据对象进行复制粘贴,当前支持:

  • 文本

  • HTML

三、接口说明

(一)基础数据类型接口

主要接口如下:

接口名称 说明
setData(data, callback) 将数据写入系统剪贴板(异步回调)
setData(data) 将数据写入系统剪贴板(Promise方式)
getData(callback) 读取系统剪贴板内容(异步回调)
getData() 读取系统剪贴板内容(Promise方式)
getDataSync() 读取系统剪贴板内容(同步接口)

注意事项

  • getDataSync()不能与setData()在同线程调用

  • 获取URI类型数据后,需使用文件管理的fs.copy接口获取文件

(二)统一数据类型接口

主要接口如下:

接口名称 说明
setUnifiedData(data) 将统一数据对象写入系统剪贴板(Promise)
setUnifiedDataSync(data) 将统一数据对象写入系统剪贴板(同步)
getUnifiedData() 从系统剪贴板读取统一数据对象(Promise)
getUnifiedDataSync() 从系统剪贴板读取统一数据对象(同步)

四、代码示例

(一)基础数据类型

1. 设置纯文本数据到剪贴板
import { BusinessError, pasteboard } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();

export async function setPlainData(content: string): Promise<void> {
    // 创建剪贴板数据,指定为纯文本类型
    let pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, content);
    
    // 将数据写入系统剪贴板
    await systemPasteboard.setData(pasteData);
}
2. 从剪贴板获取纯文本数据
export async function getPlainData(): Promise<string> {
    // 从系统剪贴板中读取数据
    let data = await systemPasteboard.getData();
    
    // 获取剪贴板数据中的条目数量
    let recordCount = data.getRecordCount();
    
    // 遍历所有记录,拼接文本内容
    let result = '';
    for (let i = 0; i < recordCount; i++) {
        let record = data.getRecord(i).toPlainText();
        hilog.info(0xFF00, '[Sample_pasteboard]', 'Get data success, record:' + record);
        result += record;
    }
    
    return result;
}

(二)统一数据类型示例

1. 构造并设置统一数据类型数据
import { BusinessError, pasteboard } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { unifiedDataChannel, uniformDataStruct, uniformTypeDescriptor } from '@kit.ArkData';

const systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();

export async function handleUniformData() {
    // 1. 构造一条PlainText数据
    let plainText: uniformDataStruct.PlainText = {
        uniformDataType: uniformTypeDescriptor.UniformDataType.PLAIN_TEXT,
        textContent: 'PLAINTEXT_CONTENT',
        abstract: 'PLAINTEXT_ABSTRACT',
    }
    
    // 创建统一数据记录
    let record = new unifiedDataChannel.UnifiedRecord(
        uniformTypeDescriptor.UniformDataType.PLAIN_TEXT, 
        plainText
    );
    
    // 创建统一数据对象
    let data = new unifiedDataChannel.UnifiedData();
    data.addRecord(record);
    
    // 2. 向系统剪贴板中存入PlainText数据
    systemPasteboard.setUnifiedData(data).then((data: void) => {
        hilog.info(0xFF00, '[Sample_pasteboard]', 'Succeeded in setting UnifiedData.');
        // 存入成功,处理正常场景
    }).catch((err: BusinessError) => {
        hilog.error(0xFF00, '[Sample_pasteboard]', 'Failed to set UnifiedData. Cause: ' + err.message);
        // 处理异常场景
    });
    
    // 3. 从系统剪贴板中读取text数据
    systemPasteboard.getUnifiedData().then((data) => {
        let records: unifiedDataChannel.UnifiedRecord[] = data.getRecords();
        
        for (let j = 0; j < records.length; j++) {
            if (records[j].getType() === uniformTypeDescriptor.UniformDataType.PLAIN_TEXT) {
                let text = records[j].getValue() as uniformDataStruct.PlainText;
                hilog.info(0xFF00, '[Sample_pasteboard]', `${j + 1}.${text.textContent}`);
            }
        }
    }).catch((err: BusinessError) => {
        hilog.error(0xFF00, '[Sample_pasteboard]', 'Failed to get UnifiedData. Cause: ' + err.message);
        // 处理异常场景
    });
}
2. 同步接口使用
// 使用同步接口设置统一数据
function setUnifiedDataSyncExample() {
    let plainText: uniformDataStruct.PlainText = {
        uniformDataType: uniformTypeDescriptor.UniformDataType.PLAIN_TEXT,
        textContent: '同步设置示例',
        abstract: '同步接口演示',
    }
    
    let record = new unifiedDataChannel.UnifiedRecord(
        uniformTypeDescriptor.UniformDataType.PLAIN_TEXT, 
        plainText
    );
    
    let data = new unifiedDataChannel.UnifiedData();
    data.addRecord(record);
    
    // 同步设置数据
    systemPasteboard.setUnifiedDataSync(data);
}

// 使用同步接口获取统一数据
function getUnifiedDataSyncExample() {
    let data = systemPasteboard.getUnifiedDataSync();
    let records = data.getRecords();
    
    records.forEach((record, index) => {
        hilog.info(0xFF00, '[Sample_pasteboard]', `记录${index + 1}: 类型=${record.getType()}`);
    });
}

(三)其他数据类型操作

1. 设置和获取HTML数据
export async function setHtmlData(htmlContent: string): Promise<void> {
    let pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_HTML, htmlContent);
    await systemPasteboard.setData(pasteData);
}

export async function getHtmlData(): Promise<string> {
    let data = await systemPasteboard.getData();
    let htmlContent = '';
    
    for (let i = 0; i < data.getRecordCount(); i++) {
        htmlContent += data.getRecord(i).toHtmlText();
    }
    
    return htmlContent;
}
2. URI数据处理
import { fileIo } from '@kit.CoreFileKit';

export async function copyFileFromUri(uri: string, destPath: string): Promise<void> {
    // 设置URI到剪贴板
    let pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_URI, uri);
    await systemPasteboard.setData(pasteData);
    
    // 从剪贴板获取URI并复制文件
    let data = await systemPasteboard.getData();
    if (data.getRecordCount() > 0) {
        let fileUri = data.getRecord(0).toUri();
        // 使用文件管理接口复制文件
        await fileIo.copy(fileUri, destPath);
    }
}
3. PixelMap图片数据处理
import { BusinessError, pasteboard } from '@kit.BasicServicesKit';
import { image } from '@kit.ImageKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();

// 设置PixelMap到剪贴板
export async function setPixelMapToClipboard(pixelMap: image.PixelMap): Promise<void> {
    try {
        // 创建包含PixelMap的剪贴板数据
        let pasteData = pasteboard.createData(pasteboard.MIMETYPE_PIXELMAP, pixelMap);
        
        // 可选:添加纯文本描述,供不支持PixelMap的应用使用
        pasteData.addTextRecord('图片已复制');
        
        await systemPasteboard.setData(pasteData);
        hilog.info(0xFF00, '[Sample_pasteboard]', 'PixelMap已成功复制到剪贴板');
    } catch (error) {
        hilog.error(0xFF00, '[Sample_pasteboard]', '设置PixelMap失败: ' + error.message);
        throw error;
    }
}

// 从剪贴板获取PixelMap
export async function getPixelMapFromClipboard(): Promise<image.PixelMap | null> {
    try {
        let data = await systemPasteboard.getData();
        let recordCount = data.getRecordCount();
        
        for (let i = 0; i < recordCount; i++) {
            let record = data.getRecord(i);
            
            // 检查是否为PixelMap类型
            if (record.getMimeType() === pasteboard.MIMETYPE_PIXELMAP) {
                let pixelMap = record.toPixelMap();
                hilog.info(0xFF00, '[Sample_pasteboard]', '成功获取PixelMap');
                return pixelMap;
            }
        }
        
        hilog.warn(0xFF00, '[Sample_pasteboard]', '剪贴板中没有PixelMap数据');
        return null;
    } catch (error) {
        hilog.error(0xFF00, '[Sample_pasteboard]', '获取PixelMap失败: ' + error.message);
        throw error;
    }
}

// 从资源创建PixelMap并复制
export async function copyImageResourceToClipboard(resourceId: number): Promise<void> {
    try {
        // 创建ImageSource
        let imageSource = image.createImageSource(`/resources/base/media/image${resourceId}.png`);
        
        // 创建PixelMap
        let decodeOptions: image.DecodingOptions = {
            sampleSize: 1,
            desiredSize: { width: 800, height: 600 }, // 可选:调整大小
            desiredRegion: { size: { width: 800, height: 600 }, x: 0, y: 0 },
            rotateDegrees: 0
        };
        
        let pixelMap = await imageSource.createPixelMap(decodeOptions);
        
        // 复制到剪贴板
        await setPixelMapToClipboard(pixelMap);
        
        // 释放资源
        imageSource.release();
    } catch (error) {
        hilog.error(0xFF00, '[Sample_pasteboard]', '复制图片资源失败: ' + error.message);
        throw error;
    }
}

五、注意事项

1. 建议

  • 使用基础数据类型方案实现复制粘贴功能

  • 如果需要跨应用数据交互,考虑使用统一数据类型

2. 数据类型

  • 注意ArkTS与NDK接口支持类型的差异

  • Want类型在NDK接口中不受支持

3. 权限

  • 从API version 12开始需关注剪贴板读取权限管控

  • 确保在AndroidManifest.xml中声明必要的权限

4. 其他

  • 注意剪贴板数据大小限制(128MB默认上限)

  • 同步接口getDataSync()不能与setData()在同线程调用

  • 处理URI数据时使用正确的文件操作接口

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐