单词“多点触摸”得到周围颇有几分抛出它并不总是很清楚什么人是指。对一些人来说是关于硬件的能力,对其他人而言,是指特定的手势支持的软件。无论你决定称呼它,今天我们来看看如何让你的应用程序,并享有与屏幕上的多个手指表现很好。
Copy and Paste
Android提供复制和粘贴功能强大的基于剪贴板的框架。它支持简单和复杂的数据类型,包括文本串,复杂的数据结构,文本和二进制数据流,甚至应用的资产。简单的文本数据被直接存储在剪贴板中,而复杂的数据被存储作为该粘贴应用程序与内容提供商解析的引用。复制和粘贴都在应用程序中,并实现了框架的应用程序之间进行。
由于框架的一部分使用内容提供商,这个主题假定一些熟悉Android的内容提供商的API,这是在主题内容提供商描述。
剪贴板框架
当您使用剪贴板框架,你把数据转化为剪辑对象,然后把剪辑对象在全系统剪贴板。剪辑对象可以采取三种形式:
文本
文本字符串。您可以直接把串入剪辑对象,您然后将复制到剪贴板。要粘贴字符串,将剪贴板中的剪辑对象和字符串复制到您的应用程序的存储。
URI
代表任何形式的URI的Uri对象。这主要是用于从内容提供者复制复杂的数据。要复制的数据,你把一个Uri对象到剪辑对象,并把剪辑对象复制到剪贴板。要粘贴数据,你得到的剪辑对象,得到Uri对象,它解析为数据源,如内容提供商,以及从源头到应用程序的存储复制数据。
意图
意图。这支持复制应用程序快捷方式。要复制的数据,创建一个意图,把它变成一个剪辑对象,并把剪辑对象复制到剪贴板。要粘贴数据,你得到的剪辑对象,然后意图对象复制到你的应用程序的内存区域。
剪贴板只拥有一次剪辑对象。当应用程序将一个剪辑对象在剪贴板上,以前的剪辑对象消失。
如果你希望允许用户将数据粘贴到你的应用程序,你不必处理所有类型的数据。给你的用户将其粘贴选项之前,您可以检查剪贴板上的数据。除了具有一定的数据格式,剪辑对象也包含了告诉你什么MIME类型或类型可用的元数据。该元数据可以帮助你决定是否你的应用程序可以做一些与剪贴板数据非常有用。例如,如果你有一个应用程序,主要处理可能要忽略包含URI或意图剪辑对象的文本。
您也可以允许用户将文本粘贴无论在剪贴板上的数据的形式。要做到这一点,您可以强制剪贴板数据转换成文本表示,然后粘贴这个文本。这是一节中所述强制转换剪贴板文本。
剪贴板类
本节介绍剪贴板框架使用的类。
ClipboardManager
在Android系统中,系统剪贴板是由全球ClipboardManager类表示。您不要直接实例化这个类;相反,你会得到通过调用getSystemService(CLIPBOARD_SERVICE)对它的引用。
ClipData,ClipData.Item和ClipDescription
将数据添加到剪贴板中,您可以创建包含一个数据的描述和数据本身ClipData对象。剪贴板拥有在同一时间只有一个ClipData。一个ClipData包含ClipDescription对象和一个或多个ClipData.Item对象。
一个ClipDescription对象包含有关片段元数据。尤其是,它含有可用MIME类型的片段的数据数组。当你把一个剪辑在剪贴板,这个数组可用于粘贴的应用程序,它可以检查它,看看他们是否可以处理任何可用的MIME类型。
一个ClipData.Item对象包含文本,URI或意向数据:
文本
一个CharSequence的。
URI
一个URI。这通常包含的内容提供者的URI,尽管任何URI被允许的。提供数据的应用程序将URI剪贴板上。希望粘贴数据的应用程序从剪贴板中得到URI并用它来访问内容提供商(或其他数据源)和检索数据。
意图
意图。此数据类型可以让你的应用程序快捷方式复制到剪贴板。然后,用户可以粘贴快捷方式到他们的应用程序供以后使用。
您可以到剪辑添加多个ClipData.Item对象。这允许用户多重选择作为一个剪辑复制和粘贴。例如,如果你有一个列表小工具,允许用户同时选择多个项目,可以将所有的项目一次复制到剪贴板。要做到这一点,您可以为每个项目单独ClipData.Item,然后你ClipData.Item对象添加到ClipData对象。
ClipData方便的方法
该ClipData类为创建一个单一ClipData.Item对象和一个简单的ClipDescription对象ClipData对象的静态便捷方法:
newPlainText(标签,文本)
返回ClipData对象,其单ClipData.Item对象包含的文本字符串。该ClipDescription对象的标签设置标签。在ClipDescription单MIME类型是MIMETYPE_TEXT_PLAIN。
使用newPlainText()来创建一个文本字符串的剪辑。
newUri(解析器,标签,URI)
返回ClipData对象,其单ClipData.Item对象包含一个URI。该ClipDescription对象的标签设置标签。如果URI是内容的URI(Uri.getScheme()返回内容:),该方法使用在分解器提供的ContentResolver的目的是从内容提供商检索可用的MIME类型,并将它们存储在ClipDescription。对于一个URI不是内容:URI,该方法将MIME类型MIMETYPE_TEXT_URILIST。
使用newUri()创建从URI,特别是内容的片段:URI。
newIntent(标签,意图)
返回ClipData对象,其单ClipData.Item对象包含一个意图。该ClipDescription对象的标签设置标签。 MIME类型设置为MIMETYPE_TEXT_INTENT。
使用newIntent()从一个Intent对象创建一个剪辑。
强迫剪贴板数据为文本
即使您的应用程序只能处理文字,你可以通过使用方法ClipData.Item.coerceToText转换从剪贴板复制非文本数据()。
此方法转换在ClipData.Item数据为文本,并返回一个的CharSequence。该ClipData.Item.coerceToText()返回的值是在ClipData.Item数据的形式:
文本
如果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()返回。
意图
如果ClipData.Item是一个Intent(getIntent()不为null),coerceToText()将其转换为一个Intent URI并返回它。的表示是相同的,通过Intent.toUri(URI_INTENT_SCHEME)返回。
剪贴板框架总结于图1中要复制的数据,应用程序将一个ClipData对象上ClipboardManager全局剪贴板。所述ClipData包含一个或多个ClipData.Item对象,一个ClipDescription对象。要粘贴数据,应用程序获取ClipData,从ClipDescription得到它的MIME类型,并且无论是从ClipData.Item或提供程序通过ClipData.Item提到的内容获取的数据。
图1. Android的剪贴板框架
复制到剪贴板
如前所述,将数据复制到剪贴板,你得到一个处理全球ClipboardManager对象,创建一个ClipData对象,添加剪辑描述以及一个或多个ClipData.Item对象给它,并添加完成ClipData反对ClipboardManager对象。这在下面详细过程描述:
如果您使用的是内容URI复制数据,建立一个内容提供商。
该笔记本示例应用程序是使用复制和粘贴内容提供商的一个例子。记事本Provider类实现了内容提供商。记事本类定义了供应商和其他应用程序,包括支持的MIME类型之间的契约。
获取系统剪贴板:
... // if the user selects copy case R.id.menu_copy: // Gets a handle to the clipboard service. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);将数据复制到一个新的ClipData对象:
// Creates a new text clip to put on the clipboard ClipData clip = ClipData.newPlainText("simple text","Hello, World!");对于一个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);对于意向
// 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);从剪贴板粘贴
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String pasteData = "";接下来,确定是否要启用或禁用当前活动的“粘贴”选项。您应该验证剪贴板中包含一个剪辑,并可以处理由剪辑表示的数据的类型:
/ Gets the ID of the "paste" menu item MenuItem mPasteItem = 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())) { mPasteItem.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 mPasteItem.setEnabled(false); } else { // This enables the paste menu item, since the clipboard contains plain text. mPasteItem.setEnabled(true); } }将数据从复制到剪贴板。如果“粘贴”菜单项启用这一点在方案只到达,这样你就可以假定剪贴板包含纯文本。你还不知道它是否包含一个文本字符串或指向纯文本的URI。下面的代码片段测试,但它只是显示了处理纯文本格式的代码
// 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; // 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; } 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("Clipboard contains an invalid data type"); return; } }从内容URI粘贴数据
// 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"获得全局剪贴板。还可以得到一个ContentResolver的,所以你可以访问内容提供商:
// Gets a handle to the Clipboard Manager ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); // Gets a content resolver instance ContentResolver cr = getContentResolver();获取剪贴板中的主要片段,并获得其内容为URI:
// 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();测试以查看该URI是一个内容URI通过调用的getType(URI)。如果URI不指向一个有效的内容提供商此方法返回null:
// If the clipboard contains a URI reference if (pasteUri != null) { // Is this a content URI? String uriMimeType = cr.getType(pasteUri);测试以查看的内容提供商支持的MIME类型,目前的应用可以理解的。如果是这样,调用ContentResolver.query()来获取数据。返回值是一个光标:
// 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
// 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://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);如果您已经使用内容提供商,您可能要添加,表示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和代码,具体的复制和粘贴处理。
// 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"在从用户拷贝数据的活动,设置代码将数据复制到剪贴板。在回答一个副本的请求,把URI剪贴板上:
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);在内容提供商在全球范围内,建立一个URI匹配,并添加一个URI模式将匹配你把剪贴板上的URI:
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);设置
query()
方法。这种方法可以处理不同的URL模式,这取决于你如何编写代码,但只显示为剪贴板复制操作模式:
// 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. ... }设立的getType()方法返回一个适当的MIME类型复制的数据:
/ Sets up your provider's getType() method. public String getType(Uri uri) { ... switch (sUriMatcher.match(uri)) { case GET_SINGLE_CONTACT: return (MIME_TYPE_CONTACT);从内容的部分粘贴数据URI介绍了如何从剪贴板中获取内容URI,并使用它来获取和粘贴数据。