赞
踩
ClipBoard框架,会将数据放入一个剪贴对象中,然后将该剪贴对象放到系统级剪贴板中。剪贴对象可以采用以下三种形式之一:
ClipBoard一次只保留一个剪贴对象。当应用将一个剪贴对象放到剪贴板时,上一个剪贴对象会消失。
此外,您可能还希望无论剪贴板中的数据采用何种形式,用户都可以粘贴文本。为此,您可以将剪贴板数据强制转换为文本表示形式,然后粘贴相应文本
ClipboardManager:
ClipData、ClipData.Item 和 ClipDescription:
可以向一个剪切添加多个 ClipData.Item 对象。这让用户可以将多个选择复制和粘贴为一个剪切。例如,如果您有一个列表微件,可让用户一次选择多个项,则您可以同时将所有这些项复制到剪贴板。为此,您需要分别为每个列表项创建一个 ClipData.Item,然后将 ClipData.Item 对象添加到 ClipData 对象。
ClipData 类提供静态便捷方法,用于创建具有单个 ClipData.Item 对象和一个简单 ClipDescription 对象的 ClipData 对象
即使您的应用仅处理文本,您也可以从剪贴板复制非文本数据,只需使用 ClipData.Item.coerceToText() 方法对其进行转换即可。
此方法可将 ClipData.Item 中的数据转换为文本并返回一个 CharSequence。ClipData.Item.coerceToText() 返回的值基于 ClipData.Item 中的数据的形式:
Text:
如果 ClipData.Item 是一个文本(getText() 不为 null),则 coerceToText() 返回该文本。
URI:
如果 ClipData.Item 是一个 URI(getUri() 不为 null),则 coerceToText() 会尝试将其作为内容 URI 使用:
如果此 URI 是内容 URI,并且提供程序可以返回文本流,则 coerceToText() 返回文本流。
如果此 URI 是内容 URI,但提供程序不提供文本流,则 coerceToText() 返回此 URI 的一个表示形式。该表示形式与 Uri.toString() 返回的表示形式相同。
如果此 URI 不是内容 URI,则 coerceToText() 返回此 URI 的一个表示形式。该表示形式与 Uri.toString() 返回的表示形式相同。
Intent:
如果 ClipData.Item 是一个 Intent(getIntent() 不为 null),则 coerceToText() 会将其转换为 Intent URI 并返回。该表示形式与 Intent.toUri(URI_INTENT_SCHEME) 返回的表示形式相同。
如需将数据复制到剪贴板,您需要获取全局 ClipboardManager 对象的句柄,创建一个 ClipData 对象,向其中添加一个 ClipDescription 和一个或多个 ClipData.Item 对象,然后将已完成的 ClipData 对象添加到 ClipboardManager 对象。以下过程对此进行了详细介绍:
// if the user selects copy
case R.id.menu_copy:
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
对于 Text: // Creates a new text clip to put on the clipboard ClipData clip = ClipData.newPlainText("simple text", "Hello, World!"); 对于URI: 以下代码段通过将记录 ID 编码到提供程序的内容 URI 来构建 URI。在 URI 中对标识符进行编码部分对此方法进行了更详细的说明 // Creates a Uri based on a base Uri and a record ID based on the contact's last name // Declares the base URI string private static final String CONTACTS = "content://com.example.contacts"; // Declares a path string for URIs that you use to copy data private static final String COPY_PATH = "/copy"; // Declares the Uri to paste to the clipboard Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName); ... // Creates a new URI clip object. The system uses the anonymous getContentResolver() object to // get MIME types from provider. The clip object's label is "URI", and its data is // the Uri previously created. ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri); 对于 Intent: 以下代码段为应用构建一个 Intent,然后将其放入剪贴对象中: // Creates the Intent Intent appIntent = new Intent(this, com.example.demo.myapplication.class); ... // Creates a clip object with the Intent in it. Its label is "Intent" and its data is // the Intent object created previously ClipData clip = ClipData.newIntent("Intent", appIntent);
// Set the clipboard's primary clip.
clipboard.setPrimaryClip(clip);
可以通过以下方法从剪贴板中粘贴数据:获取全局剪贴板对象,获取剪贴对象,查看其数据,然后将数据从剪贴对象复制到您自己的存储空间(如果可以)。
如需粘贴纯文本,首先请获取全局剪贴板并验证它能否返回纯文本。然后获取剪贴对象,并使用 getText() 将其文本复制到您自己的存储空间,如以下过程所述:
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
String pasteData = "";
// Gets the ID of the "paste" menu item MenuItem pasteItem = menu.findItem(R.id.menu_paste); // If the clipboard doesn't contain data, disable the paste menu item. // If it does contain data, decide if you can handle the data. if (!(clipboard.hasPrimaryClip())) { pasteItem.setEnabled(false); } else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) { // This disables the paste menu item, since the clipboard has data but it is not plain text pasteItem.setEnabled(false); } else { // This enables the paste menu item, since the clipboard contains plain text. pasteItem.setEnabled(true); }
// Responds to the user selecting "paste" case R.id.menu_paste: // Examines the item on the clipboard. If getText() does not return null, the clip item contains the // text. Assumes that this application can only handle one item at a time. ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0); // Gets the clipboard as text. pasteData = item.getText(); // If the string contains data, then the paste operation is done if (pasteData != null) { return true; // The clipboard does not contain text. If it contains a URI, attempts to get data from it } else { Uri pasteUri = item.getUri(); // If the URI contains something, try to get text from it if (pasteUri != null) { // calls a routine to resolve the URI and get data from it. This routine is not // presented here. pasteData = resolveUri(Uri); return true; } else { // Something is wrong. The MIME type was plain text, but the clipboard does not contain either // text or a Uri. Report an error. Log.e(TAG, "Clipboard contains an invalid data type"); return false; } }
如果 ClipData.Item 对象包含内容 URI,并且您已确定自己可以处理它的某种 MIME 类型,请创建一个 ContentResolver,然后调用相应 Content Provider 方法以检索数据
以下过程说明了如何基于剪贴板中的内容 URI 从 Content Provider 获取数据。它会检查提供程序中是否有应用可以使用的 MIME 类型:
// Declares a MIME type constant to match against the MIME types offered by the provider
public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
// Gets a handle to the Clipboard Manager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
// Gets a content resolver instance
ContentResolver cr = getContentResolver();
// Gets the clipboard data from the clipboard
ClipData clip = clipboard.getPrimaryClip();
if (clip != null) {
// Gets the first item from the clipboard data
ClipData.Item item = clip.getItemAt(0);
// Tries to get the item's contents as a URI
Uri pasteUri = item.getUri();
// If the clipboard contains a URI reference
if (pasteUri != null) {
// Is this a content URI?
String uriMimeType = cr.getType(pasteUri);
// If the return value is not null, the Uri is a content Uri if (uriMimeType != null) { // Does the content provider offer a MIME type that the current application can use? if (uriMimeType.equals(MIME_TYPE_CONTACT)) { // Get the data from the content provider. Cursor pasteCursor = cr.query(uri, null, null, null, null); // If the Cursor contains data, move to the first record if (pasteCursor != null) { if (pasteCursor.moveToFirst()) { // get the data from the Cursor here. The code will vary according to the // format of the data model. } } // close the Cursor pasteCursor.close(); } } } }
如需粘贴 Intent,首先请获取全局剪贴板。检查 ClipData.Item 对象以了解它是否包含 Intent。然后调用 getIntent(),以将相应 Intent 复制到您自己的存储空间。以下代码段演示了此过程:
// Gets a handle to the Clipboard Manager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
// Checks to see if the clip item contains an Intent, by testing to see if getIntent() returns null
Intent pasteIntent = clipboard.getPrimaryClip().getItemAt(0).getIntent();
if (pasteIntent != null) {
// handle the Intent
} else {
// ignore the clipboard, or issue an error if your application was expecting an Intent to be
// on the clipboard
}
Content Provider 支持复制数据库记录或文件流等复杂的数据。如需复制这类数据,您需要将一个内容 URI 放到剪贴板中。然后,粘贴应用会从剪贴板获取此 URI,并使用它来检索数据库数据或文件流描述符。
由于粘贴应用只有您的数据的内容 URI,因此它需要知道要检索哪一部分数据。您可以通过在 URI 本身中对数据的标识符进行编码来提供此信息,也可以提供一个会返回您要复制的数据的唯一 URI。选择哪种方法取决于数据的组织方式。
一种将数据复制到包含 URI 的剪贴板的实用方法是,在 URI 本身中对数据的标识符进行编码。然后,您的 Content Provider 便可以从 URI 中获取相应标识符,并使用它来检索数据。粘贴应用无需知道该标识符是否存在;它只需从剪贴板中获取您的“引用”(URI 和标识符),将其提供给 Content Provider,然后获取数据。
通常情况下,您可以通过将标识符连接到内容 URI 的末尾,将标识符编码到内容 URI 中。例如,假设您将提供程序 URI 设定为以下字符串:
"content://com.example.contacts"
如果您希望将某个名称编码到此 URI,则应使用以下代码段:
String uriString = "content://com.example.contacts" + "/" + "Smith";
// uriString now contains content://com.example.contacts/Smith.
// Generates a uri object from the string representation
Uri copyUri = Uri.parse(uriString);
如果您已经在使用 Content Provider,不妨添加一个新的 URI 路径来指明该 URI 用于复制用途。例如,假设您已拥有以下 URI 路径:
"content://com.example.contacts"/people
"content://com.example.contacts"/people/detail
"content://com.example.contacts"/people/images
您可以再添加一个专用于复制 URI 的路径:
"content://com.example.contacts/copying"
然后,您可以通过模式匹配检测到一个“复制”URI,并使用专用于复制和粘贴的代码处理该 URI。
如果您已经在使用 Content Provider、内部数据库或内部表来整理数据,那么通常会使用该编码方法。在这些情况下,您有多份要复制的数据,且可能每份数据都有一个唯一标识符。为响应来自粘贴应用的查询,您可以按数据标识符查找数据并返回。
如果您没有多份数据,则可能不需要对标识符进行编码。您可以仅使用一个专属于您的提供程序的 URI。为响应查询,您的提供程序会返回它当前包含的数据。
介绍了如何从剪贴板获取内容 URI 并使用它来获取和粘贴数据。
您应设置一个用于复制和粘贴复杂数据的 Content Provider 作为 ContentProvider 组件的子类。您还应对放到剪贴板中的 URI 进行编码,使其指向您要提供的确切记录。此外,您还必须考虑应用的现有状态:
请注意,您无需拥有任何其他 Content Provider 方法(例如 insert() 或 update())。粘贴应用只需获取受支持的 MIME 类型,并从您的提供程序复制数据。如果您已拥有这些方法,它们不会干扰复制操作。
以下代码段演示了如何设置应用以复制复杂的数据:
// Declares the base URI string
private static final String CONTACTS = "content://com.example.contacts";
// Declares a path string for URIs that you use to copy data
private static final String COPY_PATH = "/copy";
// Declares a MIME type for the copied data
public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
public class MyCopyActivity extends Activity { ... // The user has selected a name and is requesting a copy. case R.id.menu_copy: // Appends the last name to the base URI // The name is stored in "lastName" uriString = CONTACTS + COPY_PATH + "/" + lastName; // Parses the string into a URI Uri copyUri = Uri.parse(uriString); // Gets a handle to the clipboard service. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri); // Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
public class MyCopyProvider extends ContentProvider {
...
// A Uri Match object that simplifies matching content URIs to patterns.
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// An integer to use in switching based on the incoming URI pattern
private static final int GET_SINGLE_CONTACT = 0;
...
// Adds a matcher for the content URI. It matches
// "content://com.example.contacts/copy/*"
sUriMatcher.addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT);
// Sets up your provider's query() method. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ... // Switch based on the incoming content URI switch (sUriMatcher.match(uri)) { case GET_SINGLE_CONTACT: // query and return the contact for the requested name. Here you would decode // the incoming URI, query the data model based on the last name, and return the result // as a Cursor. ... }
// Sets up your provider's getType() method.
public String getType(Uri uri) {
...
switch (sUriMatcher.match(uri)) {
case GET_SINGLE_CONTACT:
return (MIME_TYPE_CONTACT);
...
}
}
final ClipboardManager cm = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData data = cm.getPrimaryClip();
// ClipData 里保存了一个ArryList 的 Item 序列, 可以用 getItemCount() 来获取个数
ClipData.Item item = data.getItemAt(0);
String text = item.getText().toString();// 注意 item.getText 可能为空
final ClipboardManager cm = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
cm.addPrimaryClipChangedListener(new ClipboardManager.OnPrimaryClipChangedListener() {
@Override
public void onPrimaryClipChanged() {
ClipData data = cm.getPrimaryClip();
ClipData.Item item = data.getItemAt(0);
String text = item.getText().toString();
Log.i(TAG, text);
}
});
private void setClipDate() {
final ClipboardManager cm = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clipData = cm.getPrimaryClip();
clipData.addItem(new ClipData.Item("hello"));
cm.setPrimaryClip(clipData);
}
这样添加的时候,在数据结构里是一个一个 Item 的形式,但是在获取的时候,例如粘贴,则会把所有 Item 拼接一起复制出来
public static String getPathFromUri(final Context context, final Uri uri) { if (uri == null) { return null; } // Check whether the version is later than Android 4.4 final boolean after44 = Build.VERSION.SDK_INT >= 19; if (after44 && DocumentsContract.isDocumentUri(context, uri)) { // If it is later than Android 4.4 and belongs to the file URI final String authority = uri.getAuthority(); // Check whether Authority is used by local files if ("com.android.externalstorage.documents".equals(authority)) { // External storage space final String docId = DocumentsContract.getDocumentId(uri); final String[] divide = docId.split(":"); final String type = divide[0]; if ("primary".equals(type)) { String path = Environment.getExternalStorageDirectory().getAbsolutePath().concat("/").concat(divide[1]); return path; } else { String path = "/storage/".concat(type).concat("/").concat(divide[1]); return path; } } else if ("com.android.providers.downloads.documents".equals(authority)) { // Download directory final String docId = DocumentsContract.getDocumentId(uri); if (docId.startsWith("raw:")) { final String path = docId.replaceFirst("raw:", ""); return path; } final Uri downloadUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(docId)); String path = queryAbsolutePath(context, downloadUri); return path; } else if ("com.android.providers.media.documents".equals(authority)) { //image,video,audio final String docId = DocumentsContract.getDocumentId(uri); final String[] divide = docId.split(":"); final String type = divide[0]; Uri mediaUri = null; if ("image".equals(type)) { mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { mediaUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { mediaUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } else { return null; } mediaUri = ContentUris.withAppendedId(mediaUri, Long.parseLong(divide[1])); String path = queryAbsolutePath(context, mediaUri); return path; } } else { // generic URI final String scheme = uri.getScheme(); String path = null; if ("content".equals(scheme)) { path = queryAbsolutePath(context, uri); } else if ("file".equals(scheme)) { path = uri.getPath(); } return path; } return null; } public static String queryAbsolutePath(final Context context, final Uri uri) { final String[] projection = {MediaStore.MediaColumns.DATA}; Cursor cursor = null; try { cursor = context.getContentResolver().query(uri, projection, null, null, null); if (cursor != null && cursor.moveToFirst()) { final int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA); return cursor.getString(index); } } catch (final Exception ex) { ex.printStackTrace(); if (cursor != null) { cursor.close(); } } return null; }
可能某些机型会存在download返回的path始终为null。这是因为不同机型下载目录不一致导致的:
“content://downloads/public_downloads”,
“content://downloads/my_downloads”,
“content://downloads/all_downloads”
public static Uri getUriFromPath(Context context, String path) { Uri mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; Cursor cursor = context.getContentResolver().query(mediaUri, null, MediaStore.Images.Media.DISPLAY_NAME + "= ?", new String[] {path.substring(path.lastIndexOf("/") + 1)}, null); Uri uri = null; if(cursor.moveToFirst()) { uri = ContentUris.withAppendedId(mediaUri, cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID))); } cursor.close(); return uri; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。