介绍
Android提供了一个强大的基于剪贴板的复制和粘贴框架。它既支持简单的数据类型,也支持复杂的数据类型,包括文本字符串、复杂的数据结构、文本和二进制流数据,甚至还支持应用程序资源。
如下图所示:
由上图可以简单的得到Android剪切板模版主要由四个类构成:ClipboardManager
、ClipData
、ClipData.Item
、ClipDescription
.
简单的描述:系统复制数据,就是创建一个
ClipData
对象放在ClipboardManager
全局上.ClipData
可以包括多条Item
子数据,子数据中复制内容可以是text
,url
,intent
,但是都是这些子数据都是来自一次复制,每次复制会覆盖之前的复制内容.同时,ClipData
中包含一个ClipDescription
,用于描述本次复制内容的MimeType.
核心类
ClipboardManager
系统服务全局的剪切板类.如何得到如下:
ClipboardManager mClipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
定义当剪贴板上的主剪辑发生更改时调用的侦听器回调:OnPrimaryClipChangedListener
.
// 添加剪贴板数据改变监听器
mClipboardManager.addPrimaryClipChangedListener(new ClipboardManager.OnPrimaryClipChangedListener() {
@Override
public void onPrimaryClipChanged() {
// 剪贴板中的数据被改变,此方法将被回调
System.out.println("onPrimaryClipChanged()");
}
});
// 移除指定的剪贴板数据改变监听器
mClipboardManager.removePrimaryClipChangedListener(listener);
-
ClipData.Item
剪切板子数据类,它包含了text
、html
、Uri
或者Intent
数据,一个clip
对象可以包含一个或多个Item
对象。
一起来看看它的属性:
final CharSequence mText;
final String mHtmlText;
final Intent mIntent;
Uri mUri;
就是一个数据类.
-
ClipDescription
剪切板的描述类.包含了ClipData
对象的metadata
信息,一般情况mimeType
只有一个.
一起看看它的属性就知道干什么的类了.
public class ClipDescription implements Parcelable {
//默认的MimeTYpe
public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
public static final String MIMETYPE_TEXT_HTML = "text/html";
public static final String MIMETYPE_TEXT_URILIST = "text/uri-list";
public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
public static final String EXTRA_TARGET_COMPONENT_NAME =
"android.content.extra.TARGET_COMPONENT_NAME";
public static final String EXTRA_USER_SERIAL_NUMBER = "android.content.extra.USER_SERIAL_NUMBER";
//包含一个标签
final CharSequence mLabel;
//mimeType数组
final String[] mMimeTypes;
//可以保存额外的数据
private PersistableBundle mExtras;
......
}
一般使用前面四种:text、html、uri、intent.其中url比较特殊.如果使用Android
资源MimeType
需要由ContentResolver
提供.
什么是uri:
通用资源标志符(Universal Resource Identifier, 简称"URI")。
Uri代表要操作的数据,Android上可用的每种资源 - 图像、视频片段等都可以用Uri来表示。
Android的Uri由以下三部分组成: "content://"、数据的路径、标示ID(可选)
-
ClipData
剪切对象,在有且仅有一个剪切板对象在系统服务中.言外之意,每一次复制前一次复制内容都会消失.
一起来看看它的属性:
public class ClipData implements Parcelable {
//默认mimetype。 text/plain
static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
ClipDescription.MIMETYPE_TEXT_PLAIN };
//text/html
static final String[] MIMETYPES_TEXT_HTML = new String[] {
ClipDescription.MIMETYPE_TEXT_HTML };
//urllist
static final String[] MIMETYPES_TEXT_URILIST = new String[] {
ClipDescription.MIMETYPE_TEXT_URILIST };
//intent
static final String[] MIMETYPES_TEXT_INTENT = new String[] {
ClipDescription.MIMETYPE_TEXT_INTENT };
//剪切板描述类
final ClipDescription mClipDescription;
final Bitmap mIcon;
//用于存放剪切板子数据
final ArrayList<Item> mItems;
.......
}
创建方式:
/**
* Create a new ClipData holding data of the type
* {@link ClipDescription#MIMETYPE_TEXT_PLAIN}.
*
* @param label User-visible label for the clip data.
* @param text The actual text in the clip.
* @return Returns a new ClipData containing the specified data.
*/
static public ClipData newPlainText(CharSequence label, CharSequence text) {
Item item = new Item(text);
return new ClipData(label, MIMETYPES_TEXT_PLAIN, item);
}
/**
* Create a new ClipData holding data of the type
* {@link ClipDescription#MIMETYPE_TEXT_HTML}.
*
* @param label User-visible label for the clip data.
* @param text The text of clip as plain text, for receivers that don't
* handle HTML. This is required.
* @param htmlText The actual HTML text in the clip.
* @return Returns a new ClipData containing the specified data.
*/
static public ClipData newHtmlText(CharSequence label, CharSequence text,
String htmlText) {
Item item = new Item(text, htmlText);
return new ClipData(label, MIMETYPES_TEXT_HTML, item);
}
/**
* Create a new ClipData holding an Intent with MIME type
* {@link ClipDescription#MIMETYPE_TEXT_INTENT}.
*
* @param label User-visible label for the clip data.
* @param intent The actual Intent in the clip.
* @return Returns a new ClipData containing the specified data.
*/
static public ClipData newIntent(CharSequence label, Intent intent) {
Item item = new Item(intent);
return new ClipData(label, MIMETYPES_TEXT_INTENT, item);
}
/**
* Create a new ClipData holding a URI. If the URI is a content: URI,
* this will query the content provider for the MIME type of its data and
* use that as the MIME type. Otherwise, it will use the MIME type
* {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
*
* @param resolver ContentResolver used to get information about the URI.
* @param label User-visible label for the clip data.
* @param uri The URI in the clip.
* @return Returns a new ClipData containing the specified data.
*/
static public ClipData newUri(ContentResolver resolver, CharSequence label,
Uri uri) {
//创建item
Item item = new Item(uri);
/*获取mimeType*/
String[] mimeTypes = null;
if ("content".equals(uri.getScheme())) {
String realType = resolver.getType(uri);
mimeTypes = resolver.getStreamTypes(uri, "*/*");
if (realType != null) {
if (mimeTypes == null) {
mimeTypes = new String[] { realType };
} else {
String[] tmp = new String[mimeTypes.length + 1];
tmp[0] = realType;
System.arraycopy(mimeTypes, 0, tmp, 1, mimeTypes.length);
mimeTypes = tmp;
}
}
}
if (mimeTypes == null) {
mimeTypes = MIMETYPES_TEXT_URILIST;
}
return new ClipData(label, mimeTypes, item);
}
/**
* Create a new ClipData holding an URI with MIME type
* {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
* Unlike {@link #newUri(ContentResolver, CharSequence, Uri)}, nothing
* is inferred about the URI -- if it is a content: URI holding a bitmap,
* the reported type will still be uri-list. Use this with care!
*
* @param label User-visible label for the clip data.
* @param uri The URI in the clip.
* @return Returns a new ClipData containing the specified data.
*/
static public ClipData newRawUri(CharSequence label, Uri uri) {
//创建item
Item item = new Item(uri);
return new ClipData(label, MIMETYPES_TEXT_URILIST, item);
}
clipData
对象创建后塞入Clipboardmanager
即可:
//Clipboardmanager方法
/**
* Sets the current primary clip on the clipboard. This is the clip that
* is involved in normal cut and paste operations.
*
* @param clip The clipped data item to set.
*/
public void setPrimaryClip(ClipData clip) {
try {
if (clip != null) {
clip.prepareToLeaveProcess(true);
}
getService().setPrimaryClip(clip, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- 转换成字符串
//任何作为HTML格式返回的文本都将作为具有样式跨度的文本返回。
CharSequence coerceToStyledText(Context context);
//如果getText()是非空的,则返回该值。
//如果getUri()非null,则尝试从其内容提供程序检索其数据作为文本流。如果成功,将文本复制到字符串中并返回。如果它不是内容:URI或内容提供程序不提供文本表示,则将原始URI作为字符串返回。
//如果getIntent()非null,则将其转换为intent: URI并返回。
//否则,返回一个空字符串。
CharSequence coerceToText(Context context) ;
//如果getHtmlText()非null,则返回该值。
//如果getText()是非空的,返回它,转换为有效的HTML文本。如果此文本包含样式跨度,则使用HTML . tohtml (span)将其转换为HTML格式。
//如果getUri()非null,则尝试从其内容提供程序检索其数据作为文本流。
//如果提供程序可以提供文本/html数据,则首选该数据并按原样返回。否则,将返回任何文本/*数据并转义到HTML。
//如果它不是内容:URI或内容提供程序不提供文本表示,将返回包含到URI链接的HTML文本。
//如果getIntent()非null,则将其转换为intent: URI并以HTML链接的形式返回。
//否则,返回一个空字符串。
String coerceToHtmlText(Context context)
详细的内容可以查看官网,地址我也写出来了,在最下面,哈哈哈.
- 注意
1、剪切板只会保存最近一次复制的内容.
2、MimeType
一般只有一个.(可以有多个)
3、系统全局的剪切板,其他应用也可以使用.
工具类
package com.rnx.react.modules.clip;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import java.util.List;
/**
* @Auther: weiwei.zhang06
* @Date: 2018/12/5 18:59
*/
public class ClipboardHelper {
public static final String TAG = ClipboardHelper.class.getSimpleName();
private Context mContext;
private volatile static ClipboardHelper mInstance;
private ClipboardManager mClipboardManager;
private ClipboardHelper(Context context) {
mContext = context;
mClipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
}
/**
* 获取ClipboardUtil实例,记得初始化
*
* @return 单例
*/
public static ClipboardHelper getInstance(Context context) {
if (mInstance == null) {
synchronized (ClipboardHelper.class) {
if (mInstance == null) {
mInstance = new ClipboardHelper(context.getApplicationContext());
}
}
}
return mInstance;
}
/**
* 判断剪贴板内是否有数据
*
* @return
*/
public boolean hasPrimaryClip() {
return mClipboardManager.hasPrimaryClip();
}
/**
* 获取剪贴板中第一条String
*
* @return
*/
public String getClipText() {
if (!hasPrimaryClip()) {
return null;
}
ClipData data = mClipboardManager.getPrimaryClip();
if (data != null
&& mClipboardManager.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
return data.getItemAt(0).getText().toString();
}
return null;
}
/**
* 获取剪贴板中第一条String
*
* @param context
* @return
*/
public String getClipText(Context context) {
return getClipText(context, 0);
}
/**
* 获取剪贴板中指定位置item的string
*
* @param context
* @param index
* @return
*/
public String getClipText(Context context, int index) {
if (!hasPrimaryClip()) {
return null;
}
ClipData data = mClipboardManager.getPrimaryClip();
if (data == null) {
return null;
}
if (data.getItemCount() > index) {
return data.getItemAt(index).coerceToText(context).toString();
}
return null;
}
/**
* 将文本拷贝至剪贴板
*
* @param text
*/
public void copyText(String label, String text) {
ClipData clip = ClipData.newPlainText(label, text);
mClipboardManager.setPrimaryClip(clip);
}
/**
* 将HTML等富文本拷贝至剪贴板
*
* @param label
* @param text
* @param htmlText
*/
public void copyHtmlText(String label, String text, String htmlText) {
ClipData clip = ClipData.newHtmlText(label, text, htmlText);
mClipboardManager.setPrimaryClip(clip);
}
/**
* 将Intent拷贝至剪贴板
*
* @param label
* @param intent
*/
public void copyIntent(String label, Intent intent) {
ClipData clip = ClipData.newIntent(label, intent);
mClipboardManager.setPrimaryClip(clip);
}
/**
* 将Uri拷贝至剪贴板
* If the URI is a content: URI,
* this will query the content provider for the MIME type of its data and
* use that as the MIME type. Otherwise, it will use the MIME type
* {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
* 如 uri = "content://contacts/people",那么返回的MIME type将变成"vnd.android.cursor.dir/person"
*
* @param cr ContentResolver used to get information about the URI.
* @param label User-visible label for the clip data.
* @param uri The URI in the clip.
*/
public void copyUri(ContentResolver cr, String label, Uri uri) {
ClipData clip = ClipData.newUri(cr, label, uri);
mClipboardManager.setPrimaryClip(clip);
}
/**
* 将多组数据放入剪贴板中,如选中ListView多个Item,并将Item的数据一起放入剪贴板
*
* @param label User-visible label for the clip data.
* @param mimeType mimeType is one of them:{@link ClipDescription#MIMETYPE_TEXT_PLAIN},
* {@link ClipDescription#MIMETYPE_TEXT_HTML},
* {@link ClipDescription#MIMETYPE_TEXT_URILIST},
* {@link ClipDescription#MIMETYPE_TEXT_INTENT}.
* @param items 放入剪贴板中的数据
*/
public void copyMultiple(String label, String mimeType, List<ClipData.Item> items) {
if (items == null || items.size() == 0) {
throw new IllegalArgumentException("argument: items error");
}
int size = items.size();
ClipData clip = new ClipData(label, new String[]{mimeType}, items.get(0));
for (int i = 1; i < size; i++) {
clip.addItem(items.get(i));
}
mClipboardManager.setPrimaryClip(clip);
}
public void copyMultiple(String label, String[] mimeTypes, List<ClipData.Item> items) {
if (items == null || items.size() == 0) {
throw new IllegalArgumentException("argument: items error");
}
int size = items.size();
ClipData clip = new ClipData(label, mimeTypes, items.get(0));
for (int i = 1; i < size; i++) {
clip.addItem(items.get(i));
}
mClipboardManager.setPrimaryClip(clip);
}
public CharSequence coercePrimaryClipToText() {
if (!hasPrimaryClip()) {
return null;
}
return mClipboardManager.getPrimaryClip().getItemAt(0).coerceToText(mContext);
}
public CharSequence coercePrimaryClipToStyledText() {
if (!hasPrimaryClip()) {
return null;
}
return mClipboardManager.getPrimaryClip().getItemAt(0).coerceToStyledText(mContext);
}
public CharSequence coercePrimaryClipToHtmlText() {
if (!hasPrimaryClip()) {
return null;
}
return mClipboardManager.getPrimaryClip().getItemAt(0).coerceToHtmlText(mContext);
}
/**
* 获取当前剪贴板内容的MimeType
*
* @return 当前剪贴板内容的MimeType
*/
public String getPrimaryClipMimeType() {
if (!hasPrimaryClip()) {
return null;
}
return mClipboardManager.getPrimaryClipDescription().getMimeType(0);
}
/**
* 获取剪贴板内容的MimeType
*
* @param clip 剪贴板内容
* @return 剪贴板内容的MimeType
*/
public String getClipMimeType(ClipData clip) {
return clip.getDescription().getMimeType(0);
}
/**
* 获取剪贴板内容的MimeType
*
* @param clipDescription 剪贴板内容描述
* @return 剪贴板内容的MimeType
*/
public String getClipMimeType(ClipDescription clipDescription) {
return clipDescription.getMimeType(0);
}
/**
* 清空剪贴板
*/
public void clearClip() {
mClipboardManager.setPrimaryClip(ClipData.newPlainText(null, ""));
}
public ClipData getClipData() {
if (!hasPrimaryClip()) {
return null;
}
return mClipboardManager.getPrimaryClip();
}
}
官网: https://developer.android.com/guide/topics/text/copy-paste#java
ClipboardManger api: https://developer.android.com/reference/android/content/ClipboardManager
ClipData api: https://developer.android.com/reference/android/content/ClipData
ClipData.item api: https://developer.android.com/reference/android/content/ClipData.Item
ClipDescription api: https://developer.android.com/reference/android/content/ClipDescription