diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index c52fd6acd73c8..a2a2be9d77089 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -125,7 +125,12 @@ Creating Status Bar Notifications - + +
+ Android provides a powerful clipboard-based framework for copying and pasting. It + supports both simple and complex data types, including text strings, complex data + structures, text and binary stream data, and even application assets. Simple text data is stored + directly in the clipboard, while complex data is stored as a reference that the pasting + application resolves with a content provider. Copying and pasting works both within an + application and between applications that implement the framework. +
+ ++ Since a part of the framework uses content providers, this topic assumes some + familiarity with the Android Content Provider API, which is described in the topic + Content Providers. +
++ When you use the clipboard framework, you put data into a clip object, and then + put the clip object on the system-wide clipboard. The clip object can take one of three forms: +
++ The clipboard holds only one clip object at a time. When an application puts a clip object on + the clipboard, the previous clip object disappears. +
++ If you want to allow users to paste data into your application, you don't have to handle all + types of data. You can examine the data on the clipboard before you give users the option to + paste it. Besides having a certain data form, the clip object also contains metadata that tells + you what MIME type or types are available. This metadata helps you decide if your application + can do something useful with the clipboard data. For example, if you have an application that + primarily handles text you may want to ignore clip objects that contain a URI or Intent. +
++ You may also want to allow users to paste text regardless of the form of data on the + clipboard. To do this, you can force the clipboard data into a text representation, and then + paste this text. This is described in the section Coercing the + clipboard to text. +
++ This section describes the classes used by the clipboard framework. +
++ In the Android system, the system clipboard is represented by the global + {@link android.content.ClipboardManager} class. You do not instantiate this + class directly; instead, you get a reference to it by invoking + {@link android.content.Context#getSystemService(String) getSystemService(CLIPBOARD_SERVICE)}. +
++ To add data to the clipboard, you create a {@link android.content.ClipData} object that + contains both a description of the data and the data itself. The clipboard holds only one + {@link android.content.ClipData} at a time. A {@link android.content.ClipData} contains a + {@link android.content.ClipDescription} object and one or more + {@link android.content.ClipData.Item} objects. +
++ A {@link android.content.ClipDescription} object contains metadata about the clip. In + particular, it contains an array of available MIME types for the clip's data. When you put a + clip on the clipboard, this array is available to pasting applications, which can examine it to + see if they can handle any of available the MIME types. +
++ A {@link android.content.ClipData.Item} object contains the text, URI, or Intent data: +
++ You can add more than one {@link android.content.ClipData.Item} object to a clip. This allows + users to copy and paste multiple selections as a single clip. For example, if you have a list + widget that allows the user to select more than one item at a time, you can copy all the items + to the clipboard at once. To do this, you create a separate + {@link android.content.ClipData.Item} for each list item, and then you add the + {@link android.content.ClipData.Item} objects to the {@link android.content.ClipData} object. +
++ The {@link android.content.ClipData} class provides static convenience methods for creating + a {@link android.content.ClipData} object with a single {@link android.content.ClipData.Item} + object and a simple {@link android.content.ClipDescription} object: +
+label.
+ The single MIME type in {@link android.content.ClipDescription} is
+ {@link android.content.ClipDescription#MIMETYPE_TEXT_PLAIN}.
+ + Use +{@link android.content.ClipData#newPlainText(CharSequence,CharSequence) newPlainText()} + to create a clip from a text string. +
label.
+ If the URI is a content URI ({@link android.net.Uri#getScheme() Uri.getScheme()} returns
+ content:), the method uses the {@link android.content.ContentResolver} object
+ provided in resolver to retrieve the available MIME types from the
+ content provider and store them in {@link android.content.ClipDescription}. For a URI that
+ is not a content: URI, the method sets the MIME type to
+ {@link android.content.ClipDescription#MIMETYPE_TEXT_URILIST}.
+
+ Use
+{@link android.content.ClipData#newUri(ContentResolver, CharSequence, Uri) newUri()}
+ to create a clip from a URI, particularly a content: URI.
+
label.
+ The MIME type is set to {@link android.content.ClipDescription#MIMETYPE_TEXT_INTENT}.
+ + Use +{@link android.content.ClipData#newIntent(CharSequence, Intent) newIntent()} + to create a clip from an Intent object. +
+ Even if your application only handles text, you can copy non-text data from the + clipboard by converting it with the method + {@link android.content.ClipData.Item#coerceToText(Context) ClipData.Item.coerceToText()}. +
++ This method converts the data in {@link android.content.ClipData.Item} to text and + returns a {@link java.lang.CharSequence}. The value that + {@link android.content.ClipData.Item#coerceToText(Context) ClipData.Item.coerceToText()} + returns is based on the form of data in {@link android.content.ClipData.Item}: +
++ The clipboard framework is summarized in Figure 1. To copy data, an application puts a + {@link android.content.ClipData} object on the {@link android.content.ClipboardManager} global + clipboard. The {@link android.content.ClipData} contains one or more + {@link android.content.ClipData.Item} objects and one + {@link android.content.ClipDescription} object. To paste data, an application gets the + {@link android.content.ClipData}, gets its MIME type from the + {@link android.content.ClipDescription}, and gets the data either from + the {@link android.content.ClipData.Item} or from the content provider referred to by + {@link android.content.ClipData.Item}. +
+ +
++ Figure 1. The Android clipboard framework +
++ As described previously, to copy data to the clipboard you get a handle to the global + {@link android.content.ClipboardManager} object, create a {@link android.content.ClipData} + object, add a {@link android.content.ClipDescription} and one or more + {@link android.content.ClipData.Item} objects to it, and add the finished + {@link android.content.ClipData} object to the {@link android.content.ClipboardManager} object. + This is described in detail in the following procedure: +
++ The + Note Pad sample application is an example of using a content provider for + copying and pasting. The + + NotePadProvider class implements the content provider. The + + NotePad class defines a contract between the provider and other applications, + including the supported MIME types. +
++ +... + +// if the user selects copy +case R.id.menu_copy: + +// Gets a handle to the clipboard service. +ClipboardManager clipboard = (ClipboardManager) + getSystemService(Context.CLIPBOARD_SERVICE); ++
+ Copy the data to a new {@link android.content.ClipData} object: +
+
+// Creates a new text clip to put on the clipboard
+ClipData clip = ClipData.newPlainText("simple text","Hello, World!");
+
+ + This snippet constructs a URI by encoding a record ID onto the content URI + for the provider. This technique is covered in more detail + in the section Encoding an identifier on the 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); ++
+ This snippet constructs an Intent for an application + and then puts it in the clip object: +
+
+// 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); ++
+ As described previously, you paste data from the clipboard by getting the global clipboard + object, getting the clip object, looking at its data, and if possible copying the data from + the clip object to your own storage. This section describes in detail how to do this for + the three forms of clipboard data. +
++ To paste plain text, first get the global clipboard and verify that it can return plain text. + Then get the clip object and copy its text to your own storage using + {@link android.content.ClipData.Item#getText()}, as described in the following procedure: +
++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);
+ }
+}
+
+
+// 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;
+ }
+}
+
+ + If the {@link android.content.ClipData.Item} object contains a content URI and you + have determined that you can handle one of its MIME types, create a + {@link android.content.ContentResolver} and then call the appropriate content provider + method to retrieve the data. +
++ The following procedure describes how to get data from a content provider based on a + content URI on the clipboard. It checks that a MIME type that the application can use + is available from the provider: +
++// 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();
+
+ Uri does not point to a valid content provider:
+
+ // 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();
+ }
+ }
+ }
+}
+
+ + To paste an Intent, first get the global clipboard. Examine the + {@link android.content.ClipData.Item} object to see if it contains an Intent. Then call + {@link android.content.ClipData.Item#getIntent()} to copy the Intent to your own storage. + The following snippet demonstrates this: +
+
+// 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 providers support copying complex data such as database records or file streams. + To copy the data, you put a content URI on the clipboard. Pasting applications then get this + URI from the clipboard and use it to retrieve database data or file stream descriptors. +
++ Since the pasting application only has the content URI for your data, it needs to know which + piece of data to retrieve. You can provide this information by encoding an identifier for the + data on the URI itself, or you can provide a unique URI that will return the data you want to + copy. Which technique you choose depends on the organization of your data. +
++ The following sections describe how to set up URIs, how to provide complex data, and how to + provide file streams. The descriptions assume that you are familiar with the general principles + of content provider design. +
++ A useful technique for copying data to the clipboard with a URI is to encode an identifier for + the data on the URI itself. Your content provider can then get the identifier from the URI and + use it to retrieve the data. The pasting application doesn't have to know that the identifier + exists; all it has to do is get your "reference" (the URI plus the identifier) from + the clipboard, give it your content provider, and get back the data. +
++ You usually encode an identifier onto a content URI by concatenating it to the end of the URI. + For example, suppose you define your provider URI as the following string: +
++"content://com.example.contacts" ++
+ If you want to encode a name onto this URI, you would use the following snippet: +
++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); ++
+ If you are already using a content provider, you may want to add a new URI path that indicates + the URI is for copying. For example, suppose you already have the following URI paths: +
++"content://com.example.contacts"/people +"content://com.example.contacts"/people/detail +"content://com.example.contacts"/people/images ++
+ You could add another path that is specific to copy URIs: +
++"content://com.example.contacts/copying" ++
+ You could then detect a "copy" URI by pattern-matching and handle it with code that + is specific for copying and pasting. +
++ You normally use the encoding technique if you're already using a content provider, internal + database, or internal table to organize your data. In these cases, you have multiple pieces of + data you want to copy, and presumably a unique identifier for each piece. In response to a + query from the pasting application, you can look up the data by its identifier and return it. +
++ If you don't have multiple pieces of data, then you probably don't need to encode an identifier. + You can simply use a URI that is unique to your provider. In response to a query, your provider + would return the data it currently contains. +
+
+ Getting a single record by ID is used in the
+ Note Pad sample application to
+ open a note from the notes list. The sample uses the _id field from an SQL
+ database, but you can have any numeric or character identifier you want.
+
+ You set up a content provider for copying and pasting complex data as a subclass of the + {@link android.content.ContentProvider} component. You should also encode the URI you put on + the clipboard so that it points to the exact record you want to provide. In addition, you + have to consider the existing state of your application: +
++In the content provider, you will want to override at least the following methods: +
++ MIME types for complex data are described in the topic + Content Providers. +
++ Notice that you don't have to have any of the other content provider methods such as + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} or + {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) update()}. + A pasting application only needs to get your supported MIME types and copy data from your + provider. If you already have these methods, they won't interfere with copy operations. +
++ The following snippets demonsrate how to set up your application to copy complex data: +
++ In the global constants for your application, + declare a base URI string and a path that identifies URI strings you are + using to copy data. Also declare a MIME type for the copied data: +
++// 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);
+
+ + In the global scope of your content provider, create a URI matcher and add a URI + pattern that will match URIs you put on the clipboard: +
+
+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);
+
+ + Set up the + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) query()} + method. This method can handle different URI patterns, depending on how you code it, but + only the pattern for the clipboard copying operation is shown: +
+
+// 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.
+
+ ...
+
+}
+
+ + Set up the {@link android.content.ContentProvider#getType(Uri) getType()} method to + return an appropriate MIME type for copied data: +
+
+// Sets up your provider's getType() method.
+public String getType(Uri uri) {
+
+ ...
+
+ switch (sUriMatcher.match(uri)) {
+
+ case GET_SINGLE_CONTACT:
+
+ return (MIME_TYPE_CONTACT);
+
+ + The section Pasting data from a content URI + describes how to get a content URI from the clipboard and use it to get and paste data. +
++ You can copy and paste large amounts of text and binary data as streams. The data can have + forms such as the following: +
++ A content provider for data streams provides access to its data with a file descriptor object + such as {@link android.content.res.AssetFileDescriptor} instead of a + {@link android.database.Cursor} object. The pasting application reads the data stream using + this file descriptor. +
++ To set up your application to copy a data stream with a provider, follow these steps: +
++ To paste a data stream, an application gets the clip from the clipboard, gets the URI, and + uses it in a call to a {@link android.content.ContentResolver} file descriptor method that + opens the stream. The {@link android.content.ContentResolver} method calls the corresponding + {@link android.content.ContentProvider} method, passing it the content URI. Your provider + returns the file descriptor to {@link android.content.ContentResolver} method. The pasting + application then has the responsibility to read the data from the stream. +
++ The following list shows the most important file descriptor methods for a content provider. + Each of these has a corresponding {@link android.content.ContentResolver} method with the + string "Descriptor" appended to the method name; for example, the + {@link android.content.ContentResolver} analog of + {@link android.content.ContentProvider#openAssetFile(Uri, String) openAssetFile()} is +{@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) openAssetFileDescriptor()}: +
++ This method handles subsections of files. You can use it to read assets that the + content provider has copied to the clipboard. +
+
+ You can optionally use the
+{@link android.content.ContentProvider#openPipeHelper(Uri, String, Bundle, T, ContentProvider.PipeDataWriter) openPipeHelper()}
+ method with your file descriptor method. This allows the pasting application to read the
+ stream data in a background thread using a pipe. To use this method, you need to implement the
+ {@link android.content.ContentProvider.PipeDataWriter} interface. An example of doing this is
+ given in the Note Pad sample
+ application, in the openTypedAssetFile() method of
+ NotePadProvider.java.
+
+ To design effective copy and paste functionality for your application, remember these + points: +
++ With the Android drag/drop framework, you can allow your users to move data + from one View to another View in the current layout using a graphical drag and drop gesture. + The framework includes a drag event class, drag listeners, and helper methods and classes. +
++ Although the framework is primarily designed for data movement, you can use + it for other UI actions. For example, you could create an app that mixes colors when the user + drags a color icon over another icon. The rest of this topic, however, describes the + framework in terms of data movement. +
++ A drag and drop operation starts when the user makes some gesture that you recognize as a + signal to start dragging data. In response, your application tells the system that the drag is + starting. The system calls back to your application to get a representation of the data + being dragged. As the user's finger moves this representation (a "drag shadow") + over the current layout, the system sends drag events to the drag event listener objects and + drag event callback methods associated with the {@link android.view.View} objects in the layout. + Once the user releases the drag shadow, the system ends the drag operation. +
++ You create a drag event listener object ("listeners") from a class that implements + {@link android.view.View.OnDragListener}. You set the drag event listener object for a View + with the View object's + {@link android.view.View#setOnDragListener(View.OnDragListener) setOnDragListener()} method. + Each View object also has a {@link android.view.View#onDragEvent(DragEvent) onDragEvent()} + callback method. Both of these are described in more detail in the section + The drag event listener and callback method. +
++ Note: For the sake of simplicity, the following sections refer to the routine + that receives drag events as the "drag event listener", even though it may actually + be a callback method. +
++ When you start a drag, you include both the data you are moving and metadata describing this + data as part of the call to the system. During the drag, the system sends drag events to the + drag event listeners or callback methods of each View in the layout. The listeners or callback + methods can use the metadata to decide if they want to accept the data when it is dropped. + If the user drops the data over a View object, and that View object's listener or callback + method has previously told the system that it wants to accept the drop, then the system sends + the data to the listener or callback method in a drag event. +
++ Your application tells the system to start a drag by calling the + {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()} + method. This tells the system to start sending drag events. The method also sends the data that + you are dragging. +
++ You can call + {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()} + for any attached View in the current layout. The system only uses the View object to get access + to global settings in your layout. +
++ Once your application calls + {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}, + the rest of the process uses events that the system sends to the View objects in your current + layout. +
++ There are basically four steps or states in the drag and drop process: +
++ The system first responds by calling back to your application to get a drag shadow. It + then displays the drag shadow on the device. +
+
+ Next, the system sends a drag event with action type
+ {@link android.view.DragEvent#ACTION_DRAG_STARTED} to the drag event listeners for
+ all the View objects in the current layout. To continue to receive drag events,
+ including a possible drop event, a drag event listener must return true.
+ This registers the listener with the system. Only registered listeners continue to
+ receive drag events. At this point, listeners can also change the appearance of their
+ View object to show that the listener can accept a drop event.
+
+ If the drag event listener returns false, then it will not receive drag
+ events for the current operation until the system sends a drag event with action type
+ {@link android.view.DragEvent#ACTION_DRAG_ENDED}. By sending false, the
+ listener tells the system that it is not interested in the drag operation and
+ does not want to accept the dragged data.
+
true to
+ the system if code for accepting the drop succeeds.
+ + Note that this step only occurs if the user drops the drag shadow within the bounding + box of a View whose listener is registered to receive drag events. If the user releases + the drag shadow in any other situation, no {@link android.view.DragEvent#ACTION_DROP} + drag event is sent. +
++ Each of these four steps is described in more detail in the section + Designing a Drag and Drop Operation. +
++ A View receives drag events with either a drag event listener that implements + {@link android.view.View.OnDragListener} or with its + {@link android.view.View#onDragEvent(DragEvent)} callback method. + When the system calls the method or listener, it passes to them + a {@link android.view.DragEvent} object. +
++ You will probably want to use the listener in most cases. When you design UIs, you usually + don't subclass View classes, but using the callback method forces you to do this in order to + override the method. In comparison, you can implement one listener class and then use it with + several different View objects. You can also implement it as an anonymous inline class. To + set the listener for a View object, call +{@link android.view.View#setOnDragListener(android.view.View.OnDragListener) setOnDragListener()}. +
+
+ You can have both a listener and a callback method for View object. If this occurs,
+ the system first calls the listener. The system doesn't call the callback method unless the
+ listener returns false.
+
+ The combination of the {@link android.view.View#onDragEvent(DragEvent)} method and + {@link android.view.View.OnDragListener} is analogous to the combination + of the {@link android.view.View#onTouchEvent(MotionEvent) onTouchEvent()} and + {@link android.view.View.OnTouchListener} used with touch events. +
++ The system sends out a drag event in the form of a {@link android.view.DragEvent} object. The + object contains an action type that tells the listener what is happening in the drag/drop + process. The object contains other data, depending on the action type. +
++ To get the action type, a listener calls {@link android.view.DragEvent#getAction()}. There + are six possible values, defined by constants in the {@link android.view.DragEvent} class. These + are listed in table 1. +
++ The {@link android.view.DragEvent} object also contains the data that your application provided + to the system in the call to + {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}. + Some of the data is valid only for certain action types. The data that is valid for each action + type is summarized in table 2. It is also described in detail with + the event for which it is valid in the section + Designing a Drag and Drop Operation. +
++ Table 1. DragEvent action types +
+| getAction() value | +Meaning | +
|---|---|
| {@link android.view.DragEvent#ACTION_DRAG_STARTED} | ++ A View object's drag event listener receives this event action type just after the + application calls +{@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()} and + gets a drag shadow. + | +
| {@link android.view.DragEvent#ACTION_DRAG_ENTERED} | +
+ A View object's drag event listener receives this event action type when the drag shadow
+ has just entered the bounding box of the View. This is the first event action type the
+ listener receives when the drag shadow enters the bounding box. If the listener wants to
+ continue receiving drag events for this operation, it must return boolean
+ true to the system.
+ |
+
| {@link android.view.DragEvent#ACTION_DRAG_LOCATION} | ++ A View object's drag event listener receives this event action type after it receives a + {@link android.view.DragEvent#ACTION_DRAG_ENTERED} event while the drag shadow is + still within the bounding box of the View. + | +
| {@link android.view.DragEvent#ACTION_DRAG_EXITED} | ++ A View object's drag event listener receives this event action type after it receives a + {@link android.view.DragEvent#ACTION_DRAG_ENTERED} and at least one + {@link android.view.DragEvent#ACTION_DRAG_LOCATION} event, and after the user has moved + the drag shadow outside the bounding box of the View. + | +
| {@link android.view.DragEvent#ACTION_DROP} | +
+ A View object's drag event listener receives this event action type when the user
+ releases the drag shadow over the View object. This action type is only sent to a View
+ object's listener if the listener returned boolean true in response to the
+ {@link android.view.DragEvent#ACTION_DRAG_STARTED} drag event. This action type is not
+ sent if the user releases the drag shadow on a View whose listener is not registered,
+ or if the user releases the drag shadow on anything that is not part of the current
+ layout.
+
+ The listener is expected to return boolean |
+
| {@link android.view.DragEvent#ACTION_DRAG_ENDED} | +
+ A View object's drag event listener receives this event action type
+ when the system is ending the drag operation. This action type is not necessarily
+ preceded by an {@link android.view.DragEvent#ACTION_DROP} event. If the system sent
+ a {@link android.view.DragEvent#ACTION_DROP}, receiving the
+ {@link android.view.DragEvent#ACTION_DRAG_ENDED} action type does not imply that the
+ drop operation succeeded. The listener must call
+ {@link android.view.DragEvent#getResult()} to get the value that was
+ returned in response to {@link android.view.DragEvent#ACTION_DROP}. If an
+ {@link android.view.DragEvent#ACTION_DROP} event was not sent, then
+ {@link android.view.DragEvent#getResult()} returns false.
+ |
+
+ Table 2. Valid DragEvent data by action type
+| {@link android.view.DragEvent#getAction()} value | +{@link android.view.DragEvent#getClipDescription()} value | +{@link android.view.DragEvent#getLocalState()} value | +{@link android.view.DragEvent#getX()} value | +{@link android.view.DragEvent#getY()} value | +{@link android.view.DragEvent#getClipData()} value | +{@link android.view.DragEvent#getResult()} value | +
|---|---|---|---|---|---|---|
| {@link android.view.DragEvent#ACTION_DRAG_STARTED} | +X | +X | +X | ++ | + | + |
| {@link android.view.DragEvent#ACTION_DRAG_ENTERED} | +X | +X | +X | +X | ++ | + |
| {@link android.view.DragEvent#ACTION_DRAG_LOCATION} | +X | +X | +X | +X | ++ | + |
| {@link android.view.DragEvent#ACTION_DRAG_EXITED} | +X | +X | ++ | + | + | + |
| {@link android.view.DragEvent#ACTION_DROP} | +X | +X | +X | +X | +X | ++ |
| {@link android.view.DragEvent#ACTION_DRAG_ENDED} | +X | +X | ++ | + | + | X | +
+ The {@link android.view.DragEvent#getAction()}, + {@link android.view.DragEvent#describeContents()}, + {@link android.view.DragEvent#writeToParcel(Parcel,int) writeToParcel()}, and + {@link android.view.DragEvent#toString()} methods always return valid data. +
+
+ If a method does not contain valid data for a particular action type, it returns either
+ null or 0, depending on its result type.
+
+ During a drag and drop operation, the system displays a image that the user drags. + For data movement, this image represents the data being dragged. For other operations, the + image represents some aspect of the drag operation. +
++ The image is called a drag shadow. You create it with methods you declare for a + {@link android.view.View.DragShadowBuilder} object, and then pass it to the system when you + start a drag using + {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}. + As part of its response to + {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}, + the system invokes the callback methods you've defined in + {@link android.view.View.DragShadowBuilder} to obtain a drag shadow. +
++ The {@link android.view.View.DragShadowBuilder} class has two constructors: +
++ If you use this constructor, you don't have to extend + {@link android.view.View.DragShadowBuilder} or override its methods. By default, + you will get a drag shadow that has the same appearance as the View you pass as an + argument, centered under the location where the user is touching the screen. +
+null).
+ If you use this constructor, and you don't extend
+ {@link android.view.View.DragShadowBuilder} or override its methods,
+ you will get an invisible drag shadow.
+ The system does not give an error.
+ + The {@link android.view.View.DragShadowBuilder} class has two methods: +
++ To improve performance, you should keep the size of the drag shadow small. For a single item, + you may want to use a icon. For a multiple selection, you may want to use icons in a stack + rather than full images spread out over the screen. +
++ This section shows step-by-step how to start a drag, how to respond to events during + the drag, how respond to a drop event, and how to end the drag and drop operation. +
++ The user starts a drag with a drag gesture, usually a long press, on a View object. + In response, you should do the following: +
+null instead of an actual object.
+ + For example, this code snippet shows how to respond to a long press on a ImageView + by creating a ClipData object that contains the tag or label of an + ImageView. Following this snippet, the next snippet shows how to override the methods in + {@link android.view.View.DragShadowBuilder}: +
+
+// Create a string for the ImageView label
+private static final String IMAGEVIEW_TAG = "icon bitmap"
+
+// Creates a new ImageView
+ImageView imageView = new ImageView(this);
+
+// Sets the bitmap for the ImageView from an icon bit map (defined elsewhere)
+imageView.setImageBitmap(mIconBitmap);
+
+// Sets the tag
+imageView.setTag(IMAGEVIEW_TAG);
+
+ ...
+
+// Sets a long click listener for the ImageView using an anonymous listener object that
+// implements the OnLongClickListener interface
+imageView.setOnLongClickListener(new View.OnLongClickListener() {
+
+ // Defines the one method for the interface, which is called when the View is long-clicked
+ public boolean onLongClick(View v) {
+
+ // Create a new ClipData.
+ // This is done in two steps to provide clarity. The convenience method
+ // ClipData.newPlainText() can create a plain text ClipData in one step.
+
+ // Create a new ClipData.Item from the ImageView object's tag
+ ClipData.Item item = new ClipData.Item(v.getTag());
+
+ // Create a new ClipData using the tag as a label, the plain text MIME type, and
+ // the already-created item. This will create a new ClipDescription object within the
+ // ClipData, and set its MIME type entry to "text/plain"
+ ClipData dragData = new ClipData(v.getTag(),ClipData.MIMETYPE_TEXT_PLAIN,item);
+
+ // Instantiates the drag shadow builder.
+ View.DrawShadowBuilder myShadow = new MyDragShadowBuilder(imageView);
+
+ // Starts the drag
+
+ v.startDrag(dragData, // the data to be dragged
+ myShadow, // the drag shadow builder
+ null, // no need to use local data
+ 0 // flags (not currently used, set to 0)
+ );
+
+ }
+}
+
+
+ private static class MyDragShadowBuilder extends View.DragShadowBuilder {
+
+ // The drag shadow image, defined as a drawable thing
+ private static Drawable shadow;
+
+ // Defines the constructor for myDragShadowBuilder
+ public MyDragShadowBuilder(View v) {
+
+ // Stores the View parameter passed to myDragShadowBuilder.
+ super(v);
+
+ // Creates a draggable image that will fill the Canvas provided by the system.
+ shadow = new ColorDrawable(Color.LTGRAY);
+ }
+
+ // Defines a callback that sends the drag shadow dimensions and touch point back to the
+ // system.
+ @Override
+ public void onProvideShadowMetrics (Point size, Point touch)
+ // Defines local variables
+ private int width, height;
+
+ // Sets the width of the shadow to half the width of the original View
+ width = getView().getWidth() / 2;
+
+ // Sets the height of the shadow to half the height of the original View
+ height = getView().getHeight() / 2;
+
+ // The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the
+ // Canvas that the system will provide. As a result, the drag shadow will fill the
+ // Canvas.
+ shadow.setBounds(0, 0, width, height);
+
+ // Sets the size parameter's width and height values. These get back to the system
+ // through the size parameter.
+ size.set(width, height);
+
+ // Sets the touch point's position to be in the middle of the drag shadow
+ touch.set(width / 2, height / 2);
+ }
+
+ // Defines a callback that draws the drag shadow in a Canvas that the system constructs
+ // from the dimensions passed in onProvideShadowMetrics().
+ @Override
+ public void onDrawShadow(Canvas canvas) {
+
+ // Draws the ColorDrawable in the Canvas passed in from the system.
+ shadow.draw(canvas);
+ }
+ }
+
+ + Note: Remember that you don't have to extend + {@link android.view.View.DragShadowBuilder}. The constructor + {@link android.view.View.DragShadowBuilder#View.DragShadowBuilder(View)} creates a + default drag shadow that's the same size as the View argument passed to it, with the + touch point centered in the drag shadow. +
++ During the drag operation, the system dispatches drag events to the drag event listeners + of the View objects in the current layout. The listeners should react + by calling {@link android.view.DragEvent#getAction()} to get the action type. + At the start of a drag, this methods returns {@link android.view.DragEvent#ACTION_DRAG_STARTED}. +
++ In response to an event with the action type {@link android.view.DragEvent#ACTION_DRAG_STARTED}, + a listener should do the following: +
++ If the drag and drop operation does not represent data movement, this may not be + necessary. +
+true. This tells
+ the system to continue to send drag events to the listener.
+ If it can't accept a drop, it should return false, and the system
+ will stop sending drag events until it sends out
+ {@link android.view.DragEvent#ACTION_DRAG_ENDED}.
+ + Note that for an {@link android.view.DragEvent#ACTION_DRAG_STARTED} event, these + the following {@link android.view.DragEvent} methods are not valid: + {@link android.view.DragEvent#getClipData()}, {@link android.view.DragEvent#getX()}, + {@link android.view.DragEvent#getY()}, and {@link android.view.DragEvent#getResult()}. +
+
+ During the drag, listeners that returned true in response to
+ the {@link android.view.DragEvent#ACTION_DRAG_STARTED} drag event continue to receive drag
+ events. The types of drag events a listener receives during the drag depend on the location of
+ the drag shadow and the visibility of the listener's View.
+
+ During the drag, listeners primarily use drag events to decide if they should change the + appearance of their View. +
++ During the drag, {@link android.view.DragEvent#getAction()} returns one of three + values: +
++ The listener does not need to react to any of these action types. If the listener returns a + value to the system, it is ignored. Here are some guidelines for responding to each of + these action types: +
++ When the user releases the drag shadow on a View in the application, and that View previously + reported that it could accept the content being dragged, the system dispatches a drag event + to that View with the action type {@link android.view.DragEvent#ACTION_DROP}. The listener + should do the following: +
+true to indicate that the drop was processed successfully, or
+ boolean false if it was not. The returned value becomes the value returned by
+ {@link android.view.DragEvent#getResult()} for an
+ {@link android.view.DragEvent#ACTION_DRAG_ENDED} event.
+
+ Note that if the system does not send out an {@link android.view.DragEvent#ACTION_DROP}
+ event, the value of {@link android.view.DragEvent#getResult()} for an
+ {@link android.view.DragEvent#ACTION_DRAG_ENDED} event is false.
+
+ For an {@link android.view.DragEvent#ACTION_DROP} event, + {@link android.view.DragEvent#getX()} and {@link android.view.DragEvent#getY()} + return the X and Y position of the drag point at the moment of the drop, using the coordinate + system of the View that received the drop. +
++ The system does allow the user to release the drag shadow on a View whose listener is not + receiving drag events. It will also allow the user to release the drag shadow + on empty regions of the application's UI, or on areas outside of your application. + In all of these cases, the system does not send an event with action type + {@link android.view.DragEvent#ACTION_DROP}, although it does send out an + {@link android.view.DragEvent#ACTION_DRAG_ENDED} event. +
++ Immediately after the user releases the drag shadow, the system sends a + drag event to all of the drag event listeners in your application, with an action type of + {@link android.view.DragEvent#ACTION_DRAG_ENDED}. This indicates that the drag operation is + over. +
++ Each listener should do the following: +
+true in response to an event of
+ action type {@link android.view.DragEvent#ACTION_DROP}, then
+ {@link android.view.DragEvent#getResult()} will return boolean true. In all
+ other cases, {@link android.view.DragEvent#getResult()} returns boolean false,
+ including any case in which the system did not send out a
+ {@link android.view.DragEvent#ACTION_DROP} event.
+ true to the system.
+ +
++ All drag events are initially received by your drag event method or listener. The following + code snippet is a simple example of reacting to drag events in a listener: +
+
+// Creates a new drag event listener
+mDragListen = new myDragEventListener();
+
+View imageView = new ImageView(this);
+
+// Sets the drag event listener for the View
+imageView.setOnDragListener(mDragListen);
+
+...
+
+protected class myDragEventListener implements View.OnDragEventListener {
+
+ // This is the method that the system calls when it dispatches a drag event to the
+ // listener.
+ public boolean onDrag(View v, DragEvent event) {
+
+ // Defines a variable to store the action type for the incoming event
+ final int action = event.getAction();
+
+ // Handles each of the expected events
+ switch(action) {
+
+ case DragEvent.ACTION_DRAG_STARTED:
+
+ // Determines if this View can accept the dragged data
+ if (event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
+
+ // As an example of what your application might do,
+ // applies a blue color tint to the View to indicate that it can accept
+ // data.
+ v.setColorFilter(Color.BLUE);
+
+ // Invalidate the view to force a redraw in the new tint
+ v.invalidate();
+
+ // returns true to indicate that the View can accept the dragged data.
+ return(true);
+
+ } else {
+
+ // Returns false. During the current drag and drop operation, this View will
+ // not receive events again until ACTION_DRAG_ENDED is sent.
+ return(false);
+
+ }
+ break;
+
+ case DragEvent.ACTION_DRAG_ENTERED: {
+
+ // Applies a green tint to the View. Return true; the return value is ignored.
+
+ v.setColorFilter(Color.GREEN);
+
+ // Invalidate the view to force a redraw in the new tint
+ v.invalidate();
+
+ return(true);
+
+ break;
+
+ case DragEvent.ACTION_DRAG_LOCATION:
+
+ // Ignore the event
+ return(true);
+
+ break;
+
+ case DragEvent.ACTION_DRAG_EXITED:
+
+ // Re-sets the color tint to blue. Returns true; the return value is ignored.
+ v.setColorFilter(Color.BLUE);
+
+ // Invalidate the view to force a redraw in the new tint
+ v.invalidate();
+
+ return(true);
+
+ break;
+
+ case DragEvent.ACTION_DROP:
+
+ // Gets the item containing the dragged data
+ ClipData.Item item = event.getClipData().getItemAt(0);
+
+ // Gets the text data from the item.
+ dragData = item.getText();
+
+ // Displays a message containing the dragged data.
+ Toast.makeText(this, "Dragged data is " + dragData, Toast.LENGTH_LONG);
+
+ // Turns off any color tints
+ v.clearColorFilter();
+
+ // Invalidates the view to force a redraw
+ v.invalidate();
+
+ // Returns true. DragEvent.getResult() will return true.
+ return(true);
+
+ break;
+
+ case DragEvent.ACTION_DRAG_ENDED:
+
+ // Turns off any color tinting
+ v.clearColorFilter();
+
+ // Invalidates the view to force a redraw
+ v.invalidate();
+
+ // Does a getResult(), and displays what happened.
+ if (event.getResult()) {
+ Toast.makeText(this, "The drop was handled.", Toast.LENGTH_LONG);
+
+ } else {
+ Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_LONG);
+
+ };
+
+ // returns true; the value is ignored.
+ return(true);
+
+ break;
+
+ // An unknown action type was received.
+ default:
+ Log.e("DragDrop Example","Unknown action type received by OnDragListener.");
+
+ break;
+ };
+ };
+};
+
\ No newline at end of file
diff --git a/docs/html/images/ui/clipboard/copy_paste_framework.png b/docs/html/images/ui/clipboard/copy_paste_framework.png
new file mode 100755
index 0000000000000..57facaab90182
Binary files /dev/null and b/docs/html/images/ui/clipboard/copy_paste_framework.png differ