Android APP 剪切板应用

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.mzph.cn/bicheng/10376.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Batch 是什么?主要用于什么场景?

Spring Batch是一个开源的、基于Spring框架的批量处理框架&#xff0c;它提供了一系列用于批量数据处理的工具和API。Spring Batch的主要目标是简化和标准化批量数据的处理过程&#xff0c;使得开发者可以更加专注于业务逻辑的实现&#xff0c;而不是批量处理的复杂性。 Sprin…

MSP430环境搭建

1.下载ccs编译器 注意&#xff1a;安装路径和工作路径不能出现中文&#xff01; 没有说明的步骤就点next即可&#xff01; 1.1下载适合自己电脑的压缩包。 下载好压缩包后解压&#xff0c;点击有图标进行安装。 1.2创建一个文件夹用于安装编译器位置 选择安装地址&#xff0…

新书速览|Rust编程与项目实战

掌握Rust编程基础和开发方法&#xff0c;实战网络编程、图像和游戏开发、数据分析项目 本书内容 Rust是一门系统编程语言&#xff0c;专注于安全&#xff0c;尤其是并发安全&#xff0c;它也是支持函数式、命令式以及泛型等编程范式的多范式语言。标准Rust在语法和性能上和标准…

PostgreSQL【提升性能篇】 coalesce()函数的奇思妙用

文章目录 前言一、在 WHERE 条件中使用 coalesce()二、在 SELECT 子句中使用 coalesce()总结前言 在数据库查询中,我们经常需要处理 NULL 值和空字符串的情况。PostgreSQL 提供了一个非常有用的函数 coalesce(),可以简化这种处理过程。 在博主的工作经验中,coalesce() 常用…

贝壳面试:MySQL联合索引,最左匹配原则是什么?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; 1.谈谈你对MySQL联合索引的认识&#xff1f; 2.在MySQ…

信创 | 高效信创项目管理:关键步骤与实用技巧!

高效信创项目管理的关键步骤与实用技巧可以从多个维度进行分析和总结。首先&#xff0c;建立有效的工程管理体系是确保信创项目顺利实施的基础&#xff0c;这包括项目管理、质量管理、成本控制等方面的工作。其次&#xff0c;实现项目全流程精细化管理&#xff0c;如信息的及时…

spring boot 核心配置文件是什么?

Spring Boot 的核心配置文件主要是 application.properties 或 application.yml&#xff08;也称为 YAML 格式&#xff09;。这两个文件通常位于项目的 src/main/resources 目录下&#xff0c;用于配置 Spring Boot 应用程序的各种属性和设置。 application.properties&#xf…

ts可以和python混合编程吗

在某种程度上&#xff0c;可以将 TypeScript&#xff08;TS&#xff09;和 Python 结合起来进行混合编程&#xff0c;但具体的实现方式取决于你的需求和技术栈。 一种常见的方法是通过 REST API 或 WebSocket 等通信方式&#xff0c;将 TypeScript 和 Python 编写的不同部分连…

使用./build.sh编译ORB_SLAM源码时出现报错:/usr/bin/ld:找不到 -lboost_serialization的解决办法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、/usr/bin/ld:找不到 -lboost_serialization1.问题描述2.解决(1). 下载源码(2) . 编译安装 一、/usr/bin/ld:找不到 -lboost_serialization 1.问题描述 在安装…

机器视觉任务中语义分割方法的进化历史

机器视觉任务中语义分割方法的进化历史 一、基于传统方法的图像分割二、基于卷积神经网络的图像分割三、基于Attention机制的图像分割四、语义分割模型的挑战与改进 在图像处理领域&#xff0c;传统图像分割技术扮演着重要角色。 一、基于传统方法的图像分割 这些方法包括大津…

2 GPIO控制

ESP32的GPIO的模式&#xff0c;一共有输入和输出模式两类。其中输入模式&#xff1a;上拉输入、下拉输入、浮空输入、模拟输入&#xff1b;输出模式&#xff1a;输出模式、开漏输出&#xff08;跟stm32八种输入输出模式有所不同&#xff09;。库函数中控制引脚的函数如下&#…

Spring AMQP的作用和用法

Spring AMQP是一个用于构建基于AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;的消息驱动的中间件框架。AMQP是一种提供高度可靠的异步消息传输协议&#xff0c;广泛用于企业级消息传递和应用程序集成。 Spring AMQP 的作用&#xff1a; 消息队列&#xff1a…

基础算法,贪心算法,贪心策略,OJ练习

文章目录 一、概念二、OJ练习2.1 区间选点2.2 区间合并2.3 区间2.4 合并果子2.5 排队接水2.6 货仓选址2.7 防晒2.8 畜栏预定2.9 雷达设备2.10 国王游戏2.11 耍杂技的牛2.12 给树染色2.13 任务2.14 能量石 三、总结 一、概念 贪心是一种在每次决策时采取当前意义下最优策略的算…

Selenium获取网页参数信息(标题、网址、网页资源)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Python正则表达式入门指南

Python中的正则表达式是处理文本数据的强大工具&#xff0c;它可以用来搜索、匹配和替换文本中的特定模式。本指南将带你入门Python正则表达式的基础知识&#xff0c;并介绍一些常用的用法。 什么是正则表达式&#xff1f; 正则表达式&#xff08;Regular Expression&#xf…

Held-Karp算法解决旅行商问题(TSP)

Held-Karp算法是一种用于解决旅行商问题&#xff08;TSP&#xff09;的动态规划算法。它由Richard M. Karp在1972年提出&#xff0c;并且是第一个证明TSP问题具有多项式时间算法的算法。Held-Karp算法利用了TSP问题的对称性和结构&#xff0c;将问题分解为更小的子问题&#xf…

Vue详细介绍

Vue.js&#xff08;通常简称为Vue&#xff09;是一个用于构建用户界面的渐进式JavaScript框架。它由尤雨溪&#xff08;Evan You&#xff09;创建&#xff0c;并于2014年首次发布。Vue的设计目的是易于上手&#xff0c;同时也能够强大到驱动复杂的单页应用&#xff08;SPA&…

linux上使用mariadb安装mysql环境

之前都是手动安装mysql数据库&#xff0c;现在尝试下在线安装&#xff0c;为后面的项目部署做准备&#xff0c;突然发现使用mariadb安装mysql环境真的超级简单。 1.使用mariadb安装mysql 安装服务端&#xff1a; yum install mariadb-server -y 安装客户端&#xff1a; yum i…

数字孪生引擎国产信创环境适配靠谱么?

近期我们组织了一次国产化环境适配以及产品国产化产品替换的交流&#xff0c;虽然从属于不同的业务条线&#xff0c;但是在过去一段时间多多少少都承受不同程度的信创压力&#xff0c;尤其是自然资源业务方面&#xff0c;由于自然资源大多数的业务是属于强GIS的范畴&#xff0c…

docker容器技术篇:rancher管理平台部署kubernetes集群

rancher管理平台部署kubernetes集群 Rancher 是一个 Kubernetes 管理工具&#xff0c;让你能在任何地方和任何提供商上部署和运行集群。 Rancher 可以创建来自 Kubernetes 托管服务提供商的集群&#xff0c;创建节点并安装 Kubernetes&#xff0c;或者导入在任何地方运行的现…