介紹
Android提供了一個(gè)強(qiáng)大的基于剪貼板的復(fù)制和粘貼框架拧烦。它既支持簡(jiǎn)單的數(shù)據(jù)類型,也支持復(fù)雜的數(shù)據(jù)類型莉擒,包括文本字符串翰蠢、復(fù)雜的數(shù)據(jù)結(jié)構(gòu)、文本和二進(jìn)制流數(shù)據(jù)啰劲,甚至還支持應(yīng)用程序資源梁沧。
如下圖所示:
由上圖可以簡(jiǎn)單的得到Android剪切板模版主要由四個(gè)類構(gòu)成:ClipboardManager
、ClipData
蝇裤、ClipData.Item
廷支、ClipDescription
.
簡(jiǎn)單的描述:系統(tǒng)復(fù)制數(shù)據(jù),就是創(chuàng)建一個(gè)
ClipData
對(duì)象放在ClipboardManager
全局上.ClipData
可以包括多條Item
子數(shù)據(jù),子數(shù)據(jù)中復(fù)制內(nèi)容可以是text
,url
,intent
,但是都是這些子數(shù)據(jù)都是來(lái)自一次復(fù)制,每次復(fù)制會(huì)覆蓋之前的復(fù)制內(nèi)容.同時(shí),ClipData
中包含一個(gè)ClipDescription
,用于描述本次復(fù)制內(nèi)容的MimeType.
核心類
ClipboardManager
系統(tǒng)服務(wù)全局的剪切板類.如何得到如下:
ClipboardManager mClipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
定義當(dāng)剪貼板上的主剪輯發(fā)生更改時(shí)調(diào)用的偵聽(tīng)器回調(diào):OnPrimaryClipChangedListener
.
// 添加剪貼板數(shù)據(jù)改變監(jiān)聽(tīng)器
mClipboardManager.addPrimaryClipChangedListener(new ClipboardManager.OnPrimaryClipChangedListener() {
@Override
public void onPrimaryClipChanged() {
// 剪貼板中的數(shù)據(jù)被改變,此方法將被回調(diào)
System.out.println("onPrimaryClipChanged()");
}
});
// 移除指定的剪貼板數(shù)據(jù)改變監(jiān)聽(tīng)器
mClipboardManager.removePrimaryClipChangedListener(listener);
-
ClipData.Item
剪切板子數(shù)據(jù)類,它包含了text
栓辜、html
恋拍、Uri
或者Intent
數(shù)據(jù),一個(gè)clip
對(duì)象可以包含一個(gè)或多個(gè)Item
對(duì)象藕甩。
一起來(lái)看看它的屬性:
final CharSequence mText;
final String mHtmlText;
final Intent mIntent;
Uri mUri;
就是一個(gè)數(shù)據(jù)類.
-
ClipDescription
剪切板的描述類.包含了ClipData
對(duì)象的metadata
信息,一般情況mimeType
只有一個(gè).
一起看看它的屬性就知道干什么的類了.
public class ClipDescription implements Parcelable {
//默認(rèn)的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";
//包含一個(gè)標(biāo)簽
final CharSequence mLabel;
//mimeType數(shù)組
final String[] mMimeTypes;
//可以保存額外的數(shù)據(jù)
private PersistableBundle mExtras;
......
}
一般使用前面四種:text施敢、html、uri狭莱、intent.其中url比較特殊.如果使用Android
資源MimeType
需要由ContentResolver
提供.
什么是uri:
通用資源標(biāo)志符(Universal Resource Identifier, 簡(jiǎn)稱"URI")僵娃。
Uri代表要操作的數(shù)據(jù),Android上可用的每種資源 - 圖像腋妙、視頻片段等都可以用Uri來(lái)表示默怨。
Android的Uri由以下三部分組成: "content://"、數(shù)據(jù)的路徑骤素、標(biāo)示ID(可選)
-
ClipData
剪切對(duì)象,在有且僅有一個(gè)剪切板對(duì)象在系統(tǒng)服務(wù)中.言外之意,每一次復(fù)制前一次復(fù)制內(nèi)容都會(huì)消失.
一起來(lái)看看它的屬性:
public class ClipData implements Parcelable {
//默認(rèn)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;
//用于存放剪切板子數(shù)據(jù)
final ArrayList<Item> mItems;
.......
}
創(chuàng)建方式:
/**
* 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) {
//創(chuàng)建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) {
//創(chuàng)建item
Item item = new Item(uri);
return new ClipData(label, MIMETYPES_TEXT_URILIST, item);
}
clipData
對(duì)象創(chuàng)建后塞入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();
}
}
- 轉(zhuǎn)換成字符串
//任何作為HTML格式返回的文本都將作為具有樣式跨度的文本返回。
CharSequence coerceToStyledText(Context context);
//如果getText()是非空的济竹,則返回該值痕檬。
//如果getUri()非null,則嘗試從其內(nèi)容提供程序檢索其數(shù)據(jù)作為文本流送浊。如果成功梦谜,將文本復(fù)制到字符串中并返回。如果它不是內(nèi)容:URI或內(nèi)容提供程序不提供文本表示,則將原始URI作為字符串返回改淑。
//如果getIntent()非null碍岔,則將其轉(zhuǎn)換為intent: URI并返回。
//否則朵夏,返回一個(gè)空字符串蔼啦。
CharSequence coerceToText(Context context) ;
//如果getHtmlText()非null,則返回該值仰猖。
//如果getText()是非空的捏肢,返回它,轉(zhuǎn)換為有效的HTML文本饥侵。如果此文本包含樣式跨度鸵赫,則使用HTML . tohtml (span)將其轉(zhuǎn)換為HTML格式。
//如果getUri()非null躏升,則嘗試從其內(nèi)容提供程序檢索其數(shù)據(jù)作為文本流辩棒。
//如果提供程序可以提供文本/html數(shù)據(jù),則首選該數(shù)據(jù)并按原樣返回膨疏。否則一睁,將返回任何文本/*數(shù)據(jù)并轉(zhuǎn)義到HTML。
//如果它不是內(nèi)容:URI或內(nèi)容提供程序不提供文本表示佃却,將返回包含到URI鏈接的HTML文本者吁。
//如果getIntent()非null,則將其轉(zhuǎn)換為intent: URI并以HTML鏈接的形式返回饲帅。
//否則复凳,返回一個(gè)空字符串。
String coerceToHtmlText(Context context)
詳細(xì)的內(nèi)容可以查看官網(wǎng),地址我也寫(xiě)出來(lái)了,在最下面,哈哈哈.
- 注意
1灶泵、剪切板只會(huì)保存最近一次復(fù)制的內(nèi)容.
2育八、MimeType
一般只有一個(gè).(可以有多個(gè))
3、系統(tǒng)全局的剪切板,其他應(yīng)用也可以使用.
工具類
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實(shí)例丘逸,記得初始化
*
* @return 單例
*/
public static ClipboardHelper getInstance(Context context) {
if (mInstance == null) {
synchronized (ClipboardHelper.class) {
if (mInstance == null) {
mInstance = new ClipboardHelper(context.getApplicationContext());
}
}
}
return mInstance;
}
/**
* 判斷剪貼板內(nèi)是否有數(shù)據(jù)
*
* @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);
}
/**
* 將多組數(shù)據(jù)放入剪貼板中,如選中ListView多個(gè)Item深纲,并將Item的數(shù)據(jù)一起放入剪貼板
*
* @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 放入剪貼板中的數(shù)據(jù)
*/
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);
}
/**
* 獲取當(dāng)前剪貼板內(nèi)容的MimeType
*
* @return 當(dāng)前剪貼板內(nèi)容的MimeType
*/
public String getPrimaryClipMimeType() {
if (!hasPrimaryClip()) {
return null;
}
return mClipboardManager.getPrimaryClipDescription().getMimeType(0);
}
/**
* 獲取剪貼板內(nèi)容的MimeType
*
* @param clip 剪貼板內(nèi)容
* @return 剪貼板內(nèi)容的MimeType
*/
public String getClipMimeType(ClipData clip) {
return clip.getDescription().getMimeType(0);
}
/**
* 獲取剪貼板內(nèi)容的MimeType
*
* @param clipDescription 剪貼板內(nèi)容描述
* @return 剪貼板內(nèi)容的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();
}
}
官網(wǎng): 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