Android APP 剪切板应用

embedded/2024/10/18 18:23:23/

1 Android剪切板简介

Android 剪贴板是一个系统级服务,它允许应用程序之间共享文本、图像、二进制数据等多种形式的信息。用户可以通过常见的复制和粘贴操作,在不同的应用之间传递数据。该设计考虑到了易用性和灵活性,使得开发者可以轻松地为自己的应用实现复制粘贴功能,同时它也强调了数据的安全性,确保剪贴板内容不会被未授权的应用访问。

接下来从剪切板的框架、数据类型处理、剪切板局限性、MIME类型说明、剪切板高效复制粘贴设计角度来先详细介绍剪切板。

剪贴板框架说明:Android的剪贴板框架由几个关键类组成,包括ClipboardManager、ClipData、ClipData.Item和ClipDescription。具体如下:

  • ClipboardManager:这是系统剪贴板的代表,通过调用getSystemService(CLIPBOARD_SERVICE)来获取对它的引用。
  • ClipData:这是一个包含数据说明(ClipDescription)和数据本身(ClipData.Item)的容器,代表了剪贴板中的一组数据。
  • ClipData.Item:这是实际的数据项,可以包含文本、URI或Intent数据。
  • ClipDescription:这个类包含关于ClipData的元数据,例如它包含的可用MIME类型数组。

数据类型处理:根据数据的类型(文本、URI、Intent等),可能需要执行不同的操作来处理或使用这些数据。例如,如果数据是文本,可以直接使用;如果数据是URI,可能需要解析它以获取实际的数据源;如果数据是Intent,可能需要执行相应的操作。

剪贴板的局限性:剪贴板只能保留一个ClipData对象。当一个新的ClipData对象被放入剪贴板时,旧的ClipData对象将被自动清除,这意味着需要确保每次只放置一个有效的ClipData对象在剪贴板上。

MIME类型说明:在Android剪贴板中,MIME类型用于表示数据的格式。例如,文本数据通常使用text/plain MIME类型,而HTML文本使用text/html。对于URI列表,使用的是text/uri-list,而对于Intent数据,则使用text/vnd.android.intent。

剪切板高效复制粘贴设计:设计有效的复制粘贴功能时,需要注意以下几点:

  • 任何时间都只有一个clip对象在剪贴板里,新的复制操作都会覆盖前一个clip对象。
  • 一个clip对象中的多个ClipData.Item对象是为了支持多选项的复制粘贴,而不是为了支持单选的多种形式。
  • 当提供数据时,可以提供不同的MIME表达方式,并将支持的MIME类型加入到ClipDescription中。
  • 安全和隐私:在使用剪贴板时,开发者应注意数据的安全性和隐私性,避免敏感信息的不当共享。

2 剪切板设计实战

实现功能:实现2个按键:一个功能是复制内容(文本和图片)到剪切板,另一个功能是从剪切板中获取粘贴内容到本地并通过TextView和Image来显示。

关于该程序,自定义 ClipboardUtils.java 的代码实现如下所示:

package com.example.myapplication3;import android.content.Context;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ClipDescription;import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicReference;import android.graphics.BitmapFactory;
import android.graphics.Bitmap;
import android.provider.MediaStore;
import android.net.Uri;
import android.util.Log;public class ClipboardUtils {private static final String TAG = "ClipboardUtils";public static final int CLIPBOARD_DATA_TYPE_TEXT = 0;public static final int CLIPBOARD_DATA_TYPE_IMAGE = 1;public static final int CLIPBOARD_DATA_TYPE_UNSUPPORT = -1;//private static final int ERROR_INDEX_OVERRIDE = -2;//private long mCallbackPtr = 0;private ClipboardManager mClipboardManager = null;//private ClipData mSetClipData = null;//private ClipData mGetClipData = null;static Context context;public ClipboardUtils() {if(context == null){Log.e(TAG,"set Content first");return;}mClipboardManager = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);if(mClipboardManager == null){Log.e(TAG, "get ClipboardManager error");}}public static void setContext(Context inContext) {context = inContext;}/*** 获取 ClipboardUtils 单例*/public static ClipboardUtils getInstance() {return Holder.sInstance;}private static class Holder { private static ClipboardUtils sInstance = new ClipboardUtils(); }public static AtomicReference<ClipData> createClipdataRef(){return new AtomicReference<>(null);}/*** 剪切板是否有数据*/public boolean hasClip() {Log.d(TAG, "java call:hasclip");return mClipboardManager.hasPrimaryClip();}/*** 清除剪切板数据*/public int clearClip() {Log.d(TAG, "java call:clearClip");mClipboardManager.clearPrimaryClip();return 0;}/*** 添加文本类型Item数据*/public int addTextItem(AtomicReference<ClipData> clipDataRef, String text){try {if (clipDataRef.get() == null) {ClipData clipData = ClipData.newPlainText("text_label", text);clipDataRef.set(clipData);}else{ClipData.Item item = ClipData.newPlainText("text_label", text).getItemAt(0);clipDataRef.get().addItem(item);}Log.e(TAG,"lenTextItem1="+clipDataRef.get().getItemCount());return 0;} catch (Exception e) {Log.e(TAG, "Error adding text item to ClipData");return -1;}}/*** 添加图片类型Item数据*/public int addImageItem(AtomicReference<ClipData> clipDataRef, Bitmap image) {try {ByteArrayOutputStream stream = new ByteArrayOutputStream();image.compress(Bitmap.CompressFormat.PNG, 100, stream);String path = MediaStore.Images.Media.insertImage(context.getContentResolver(),image,"ImageX",null);if (clipDataRef.get() == null) {ClipData clipData = ClipData.newRawUri("image_label", Uri.parse(path));clipDataRef.set(clipData);}else{ClipData.Item item = ClipData.newRawUri("image_label", Uri.parse(path)).getItemAt(0);clipDataRef.get().addItem(item);}return 0;} catch (Exception e) {Log.e(TAG, "Error adding image item to ClipData");return -1;}}/*** 根据索引获取剪贴板中的文本项*/public String getTextItem(AtomicReference<ClipData> clipDataRef, int index) {try {if (clipDataRef.get() != null && index >= 0 && index < clipDataRef.get().getItemCount()) {ClipData.Item item = clipDataRef.get().getItemAt(index);int type = getItemType(clipDataRef,index);if(type!=CLIPBOARD_DATA_TYPE_TEXT){return null;}// 直接返回文本内容,如果获取成功return item.getText().toString();}else {Log.d(TAG, "index override");}} catch (Exception e) {Log.e(TAG, "Error getting text item from ClipData");}// 如果索引无效或出现异常,返回null表示获取失败return null;}/*** 根据索引获取剪贴板中的图片项*/public Bitmap getImageItem(AtomicReference<ClipData> clipDataRef, int index) {try {if (clipDataRef.get()!= null && index >= 0 && index < clipDataRef.get().getItemCount()) {ClipData.Item item = clipDataRef.get().getItemAt(index);int type = getItemType(clipDataRef,index);if(type!=CLIPBOARD_DATA_TYPE_IMAGE){return null;}if (item.getUri() != null) {InputStream inputStream = context.getContentResolver().openInputStream(item.getUri());Bitmap bitmap = BitmapFactory.decodeStream(inputStream);if (bitmap != null) {return bitmap;}}}else {Log.d(TAG, "index override");}} catch (Exception e) {Log.e(TAG, "Error getting image item from ClipData");}return null; // 索引无效或数据类型不匹配}/*** 获取剪切板中Item的数量*/public int getItemCount(AtomicReference<ClipData> clipDataRef){return clipDataRef.get().getItemCount();}/*** 将当前的mGetClipData设置为剪贴板的主内容*/public void setPrimaryClip(AtomicReference<ClipData> clipDataRef) {if (mClipboardManager != null && clipDataRef.get() != null) {mClipboardManager.setPrimaryClip(clipDataRef.get());}}/*** 获取剪贴板中主剪贴板的内容*/public void getPrimaryClip(AtomicReference<ClipData> clipDataRef) {if (mClipboardManager != null && mClipboardManager.hasPrimaryClip()) {ClipData clipdata= mClipboardManager.getPrimaryClip();clipDataRef.set(clipdata);}}/*** 获取Item类型*/public int getItemType(AtomicReference<ClipData> clipDataRef,int index) {if (clipDataRef.get() != null && index >= 0 && index < clipDataRef.get().getItemCount()) {ClipData.Item item = clipDataRef.get().getItemAt(index);Uri uri = item.getUri();if(uri == null){return CLIPBOARD_DATA_TYPE_TEXT;}else{String mimeType = context.getContentResolver().getType(item.getUri());if (mimeType != null) {if (mimeType.startsWith("image/")) {return CLIPBOARD_DATA_TYPE_IMAGE;}else{return CLIPBOARD_DATA_TYPE_UNSUPPORT;}}}}return -2;}
}

基于对 ClipboardUtils 的调用,MainActivity.java实现为:

package com.example.myapplication3;import androidx.appcompat.app.AppCompatActivity;import android.content.ClipData;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;import java.util.concurrent.atomic.AtomicReference;public class MainActivity extends AppCompatActivity {private Button btnGetImage;private Button btnSetImage;private ImageView imageView;private TextView textView;Bitmap bitmap;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btnGetImage = findViewById(R.id.btnGetImage);btnSetImage = findViewById(R.id.btnSetImage);imageView = findViewById(R.id.imageView);textView = findViewById(R.id.textView);bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test5);//imageView.setImageBitmap(bitmap);ClipboardUtils.setContext(getApplication());// 设置点击监听器,从剪贴板获取图片btnGetImage.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {getClipFromClipboard();}});// 设置点击监听器,将图片设置到剪贴板btnSetImage.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {setClipToClipboard();}});}private void getClipFromClipboard() {AtomicReference<ClipData> clipDataRef = new AtomicReference<>(null);ClipboardUtils clipboardUtils = ClipboardUtils.getInstance();clipboardUtils.getPrimaryClip(clipDataRef);Log.e("clip","len="+clipboardUtils.getItemCount(clipDataRef));for(int i =0;i<clipboardUtils.getItemCount(clipDataRef);i++){if(clipboardUtils.getItemType(clipDataRef,i) == clipboardUtils.CLIPBOARD_DATA_TYPE_TEXT){String text = clipboardUtils.getTextItem(clipDataRef,i);textView.setText(text);}else if(clipboardUtils.getItemType(clipDataRef,i) == clipboardUtils.CLIPBOARD_DATA_TYPE_IMAGE){Bitmap bitmap1 = clipboardUtils.getImageItem(clipDataRef,i);imageView.setImageBitmap(bitmap);}else{Log.e("clip","not support format");}}}private void setClipToClipboard() {ClipboardUtils clipboardUtils = ClipboardUtils.getInstance();AtomicReference<ClipData> clipDataRef = ClipboardUtils.createClipdataRef();clipboardUtils.addTextItem(clipDataRef, "test text1");clipboardUtils.addImageItem(clipDataRef,bitmap);clipboardUtils.setPrimaryClip(clipDataRef);}
}

对应的layout xml代码配置为:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"tools:context=".MainActivity"><Buttonandroid:id="@+id/btnGetImage"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Get Image from Clipboard" /><TextViewandroid:id="@+id/textView"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="16dp"android:layout_gravity="center" /><ImageViewandroid:id="@+id/imageView"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="16dp"android:scaleType="centerInside" /><Buttonandroid:id="@+id/btnSetImage"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Set Image to Clipboard" /></LinearLayout>

最后在drawable中添加一张图片用于测试,一个基本的剪切板功能就设计完成了。


http://www.ppmy.cn/embedded/41892.html

相关文章

相约蓉城 | 全视通邀您参加 CHCC 2024第25届全国医院建设大会

第25届全国医院建设大会暨国际医院建设、装备及管理展览会&#xff08;CHCC2024&#xff09;&#xff0c;将于5月17日-19日在成都中国西部国际博览城盛大启幕。 全视通将携智慧病房、智慧门诊、智慧手术室、智慧后勤、智慧康养等产品方案亮相11号厅K05展位&#xff0c;期待与您…

如何用多个高斯泼溅合成新的场景【3DGS】

3D高斯泼溅&#xff08;3D Gaussian Splatting&#xff09;作为一种突破性摄影测量和可视化技术作为 SIGGRAPH 2023 上发表的研究论文的一部分发布。我相信3DGS是允许像你我这样的日常用户扫描 3D 的最佳现代方法并保留有机材料的精细细节&#xff0c;尤其是植物、树木、花卉和…

基于51单片机的自动浇花器电路

一、系统概述 自动浇水灌溉系统设计方案&#xff0c;以AT89C51单片机为控制核心&#xff0c;采用模块化的设计方法。 组成部分为&#xff1a;5V供电模块、土壤湿度传感器模块、ADC0832模数转换模块、水泵控制模块、按键输入模块、LCD显示模块和声光报警模块&#xff0c;结构如…

5.13网络编程

只要在一个电脑中的两个进程之间可以通过网络进行通信那么拥有公网ip的两个计算机的通信是一样的。但是一个局域网中的两台电脑上的虚拟机是不能进行通信的&#xff0c;因为这两个虚拟机在电脑中又有各自的局域网所以通信很难实现。 socket套接字是一种用于网络间进行通信的方…

uni-app 多列picker切换列显示对应内容

html部分&#xff1a; <view class"uni-list"><view class"uni-list-cell"><view class"uni-list-cell-left">选择用户</view><view class"uni-list-cell-db"><picker mode"multiSelector"…

Linux 文件目录详解

在Linux中一切皆文件&#xff0c;即使是媒体设备在Linux中存储的也是一个文件 目录 1.基本介绍 2. 具体的目录结构 1.基本介绍 Linux的文件系统是采用级层式的树状目录结构&#xff0c;在此结构中的最上层是根目录"/"&#xff0c;然后再此目录下在创建其他的目录&…

汇聚荣科技:如何有效为拼多多店铺引流?

在电商竞争激烈的今天&#xff0c;为拼多多店铺引流是每个店主必须面对的挑战。有效的引流策略不仅能增加店铺曝光度&#xff0c;还能提升转化率&#xff0c;促进销量增长。 一、社交媒体营销 利用微信、微博等社交平台进行推广&#xff0c;可以通过发布产品信息、用户评价和促…

springcloudalibaba版本发布说明

版本发布说明 | https://sca.aliyun.com 2.2.x 分支 适配 Spring Boot 为 2.4&#xff0c;Spring Cloud Hoxton 版本及以下的 Spring Cloud Alibaba 版本按从新到旧排列如下表&#xff08;最新版本用*标记&#xff09;&#xff1a; Spring Cloud Alibaba VersionSpring Cloud…