diff options
| author | Joe Malin <jmalin@google.com> | 2011-02-14 10:05:46 -0800 |
|---|---|---|
| committer | Android Git Automerger <android-git-automerger@android.com> | 2011-02-14 10:05:46 -0800 |
| commit | b2206668d1559df2f0885723e89e7f59d372c7e0 (patch) | |
| tree | f89db07a51f03652362e1f002585669a47941bbd | |
| parent | 1858c95e99f81ac46eea93288cb8866260b3d5d8 (diff) | |
| parent | 83c8b8a3284063c201beccc79f14db6bd715dd82 (diff) | |
| download | frameworks_base-b2206668d1559df2f0885723e89e7f59d372c7e0.zip frameworks_base-b2206668d1559df2f0885723e89e7f59d372c7e0.tar.gz frameworks_base-b2206668d1559df2f0885723e89e7f59d372c7e0.tar.bz2 | |
am 83c8b8a3: Merge "Doc Change: Topics for copy/paste/drag/drop" into honeycomb
* commit '83c8b8a3284063c201beccc79f14db6bd715dd82':
Doc Change: Topics for copy/paste/drag/drop
| -rw-r--r-- | docs/html/guide/guide_toc.cs | 49 | ||||
| -rw-r--r-- | docs/html/guide/topics/clipboard/copy-paste.jd | 1094 | ||||
| -rw-r--r-- | docs/html/guide/topics/ui/drag-drop.jd | 995 | ||||
| -rwxr-xr-x | docs/html/images/ui/clipboard/copy_paste_framework.png | bin | 0 -> 37996 bytes |
4 files changed, 2119 insertions, 19 deletions
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 71d9f5d..75ba1c9 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -125,7 +125,12 @@ <span class="en">Creating Status Bar Notifications</span> </a></li> </ul> - </li><!-- end of notifying the user --> + </li> + <li> + <a href="<?cs var:toroot ?>guide/topics/ui/drag-drop.html"> + Dragging and Dropping + </a><span class="new">new!</span> + </li> <li><a href="<?cs var:toroot ?>guide/topics/ui/themes.html"> <span class="en">Applying Styles and Themes</span> </a></li> @@ -248,6 +253,12 @@ </a></li> </ul> </li> + <li> + <a href="<?cs var:toroot ?>guide/topics/clipboard/copy-paste.html"> + <span class="en">Copying and Pasting</span> + </a> + <span class="new">new!</span> + </li> <li><a href="<?cs var:toroot ?>guide/topics/media/index.html"> <span class="en">Audio and Video</span> </a></li> @@ -402,24 +413,24 @@ <li class="toggle-list"> <div> <a href="<?cs var:toroot ?>guide/developing/devices/index.html"> - <span class="en">Managing Virtual Devices</span> - </a> + <span class="en">Creating and Managing Virtual Devices</span> + </a> </div> <ul> <li> <a href="<?cs var:toroot ?>guide/developing/devices/managing-avds.html"> <span class="en">With AVD Manager</span> - </a> + </a> </li> <li> <a href="<?cs var:toroot ?>guide/developing/devices/managing-avds-cmdline.html"> - <span class="en">From the Command Line</span> - </a> + <span class="en">On the Command Line</span> + </a> </li> <li> <a href="<?cs var:toroot ?>guide/developing/devices/emulator.html"> - <span class="en">Using the Android Emulator</span> - </a> + <span class="en">Using the Android Emulator</span> + </a> </li> </ul> </li> @@ -428,7 +439,7 @@ <span class="en">Using Hardware Devices</span> </a> </li> - + <li class="toggle-list"> <div> <a href="<?cs var:toroot ?>guide/developing/projects/index.html"> @@ -443,7 +454,7 @@ </li> <li> <a href="<?cs var:toroot ?>guide/developing/projects/projects-cmdline.html"> - <span class="en">From the Command Line</span> + <span class="en">On the Command Line</span> </a> </li> </ul> @@ -473,12 +484,12 @@ <ul> <li> <a href="<?cs var:toroot ?>guide/developing/debugging/debugging-projects.html"> - <span class="en">From Eclipse with ADT</span> + <span class="en">In Eclipse with ADT</span> </a> </li> <li> <a href="<?cs var:toroot ?>guide/developing/debugging/debugging-projects-cmdline.html"> - <span class="en">From Other IDEs</span> + <span class="en">In Other IDEs</span> </a> </li> <li> @@ -488,23 +499,23 @@ </li> <li> <a href="<?cs var:toroot ?>guide/developing/debugging/debugging-log.html"> - <span class="en">Reading and Writing Logs</span> + <span class="en">Reading and Writing Log Messages</span> </a> </li> <li> <a href="<?cs var:toroot ?>guide/developing/debugging/debugging-ui.html"> - <span class="en">Debugging and Profiling UIs</span> - </a> + <span class="en">Debugging and Profiling UIs</span> + </a> </li> <li> <a href="<?cs var:toroot ?>guide/developing/debugging/debugging-tracing.html"> - <span class="en">Profiling with Traceview and dmtracedump</span> - </a> + <span class="en">Profiling with Traceview and dmtracedump</span> + </a> </li> <li> <a href="<?cs var:toroot ?>guide/developing/debugging/debugging-devtools.html"> - <span class="en">Using the Dev Tools App</span> - </a> + <span class="en">Using the Dev Tools App</span> + </a> </li> </ul> </li> diff --git a/docs/html/guide/topics/clipboard/copy-paste.jd b/docs/html/guide/topics/clipboard/copy-paste.jd new file mode 100644 index 0000000..9a50a35 --- /dev/null +++ b/docs/html/guide/topics/clipboard/copy-paste.jd @@ -0,0 +1,1094 @@ +page.title=Copying and Pasting +@jd:body +<div id="qv-wrapper"> + <div id="qv"> + <h2>Quickview</h2> + <ul> + <li> + A clipboard-based framework for copying and pasting data. + </li> + <li> + Supports both simple and complex data, including text strings, complex data + structures, text and binary stream data, and application assets. + </li> + <li> + Copies and pastes simple text directly to and from the clipboard. + </li> + <li> + Copies and pastes complex data using a content provider. + </li> + <li> + Requires API 11. + </li> + </ul> + <h2>In this document</h2> + <ol> + <li> + <a href="#Clipboard">The Clipboard Framework</a> + </li> + <li> + <a href="#ClipboardClasses">Clipboard Classes</a> + <ol> + <li> + <a href="#ClipboardManager">ClipboardManager</a> + </li> + <li> + <a href="#ClipClasses"> + ClipData, ClipDescription, and ClipData.Item + </a> + </li> + <li> + <a href="#ClipDataMethods">ClipData convenience methods</a> + </li> + <li> + <a href="#CoerceToText">Coercing the clipboard data to text</a> + </li> + </ol> + </li> + <li> + <a href="#Copying">Copying to the Clipboard</a> + </li> + <li> + <a href="#Pasting">Pasting from the Clipboard</a> + <ol> + <li> + <a href="#PastePlainText">Pasting plain text</a> + </li> + <li> + <a href="#PasteContentUri">Pasting data from a content URI</a> + </li> + <li> + <a href="#PasteIntent">Pasting an Intent</a> + </li> + </ol> + </li> + <li> + <a href="#Provider">Using Content Providers to Copy Complex Data</a> + <ol> + <li> + <a href="#Encoding">Encoding an identifier on the URI</a> + </li> + <li> + <a href="#Records">Copying data structures</a> + </li> + <li> + <a href="#Streams">Copying data streams</a> + </li> + </ol> + </li> + <li> + <a href="#DataDesign">Designing Effective Copy/Paste Functionality</a> + </li> + </ol> + <h2>Key classes</h2> + <ol> + <li> + {@link android.content.ClipboardManager ClipboardManager} + </li> + <li> + {@link android.content.ClipData ClipData} + </li> + <li> + {@link android.content.ClipData.Item ClipData.Item} + </li> + <li> + {@link android.content.ClipDescription ClipDescription} + </li> + <li> + {@link android.net.Uri Uri} + </li> + <li> + {@link android.content.ContentProvider} + </li> + <li> + {@link android.content.Intent Intent} + </li> + </ol> + <h2>Related Samples</h2> + <ol> + <li> + <a href="{@docRoot}resources/samples/NotePad/index.html"> + Note Pad sample application</a> + </li> + </ol> + <h2>See also</h2> + <ol> + <li> + <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> + </li> + </ol> + </div> +</div> +<p> + 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. +</p> + +<p> + 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 + <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>. +</p> +<h2 id="Clipboard">The Clipboard Framework</h2> +<p> + 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: +</p> + <dl> + <dt>Text</dt> + <dd> + A text string. You put the string directly into the clip object, which you then put onto + the clipboard. To paste the string, you get the clip object from the clipboard and copy + the string to into your application's storage. + </dd> + <dt>URI</dt> + <dd> + A {@link android.net.Uri} object representing any form of URI. This is primarily for + copying complex data from a content provider. To copy data, you put a + {@link android.net.Uri} object into a clip object and put the clip object onto + the clipboard. To paste the data, you get the clip object, get the + {@link android.net.Uri} object, resolve it to a data source such as a content provider, + and copy the data from the source into your application's storage. + </dd> + <dt>Intent</dt> + <dd> + An {@link android.content.Intent}. This supports copying application shortcuts. To copy + data, you create an Intent, put it into a clip object, and put the clip object onto the + clipboard. To paste the data, you get the clip object and then copy the Intent object + into your application's memory area. + </dd> + </dl> +<p> + 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. +</p> +<p> + 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. +</p> +<p> + 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 <a href="#CoerceToText">Coercing the + clipboard to text</a>. +</p> +<h2 id="ClipboardClasses">Clipboard Classes</h2> +<p> + This section describes the classes used by the clipboard framework. +</p> +<h3 id="ClipboardManager">ClipboardManager</h3> +<p> + 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)}. +</p> +<h3 id="ClipClasses">ClipData, ClipData.Item, and ClipDescription</h3> +<p> + 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. +</p> +<p> + 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. +</p> +<p> + A {@link android.content.ClipData.Item} object contains the text, URI, or Intent data: +</p> +<dl> + <dt>Text</dt> + <dd> + A {@link java.lang.CharSequence}. + </dd> + <dt>URI</dt> + <dd> + A {@link android.net.Uri}. This usually contains a content provider URI, although any + URI is allowed. The application that provides the data puts the URI on the clipboard. + Applications that want to paste the data get the URI from the clipboard and use it to + access the content provider (or other data source) and retrieve the data. + </dd> + <dt>Intent</dt> + <dd> + An {@link android.content.Intent}. This data type allows you to copy an application shortcut + to the clipboard. Users can then paste the shortcut into their applications for later use. + </dd> +</dl> +<p> + 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. +</p> +<h3 id="ClipDataMethods">ClipData convenience methods</h3> +<p> + 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: +</p> +<dl> + <dt> +{@link android.content.ClipData#newPlainText(CharSequence,CharSequence) newPlainText(label, text)} + </dt> + <dd> + Returns a {@link android.content.ClipData} object whose single + {@link android.content.ClipData.Item} object contains a text string. The + {@link android.content.ClipDescription} object's label is set to <code>label</code>. + The single MIME type in {@link android.content.ClipDescription} is + {@link android.content.ClipDescription#MIMETYPE_TEXT_PLAIN}. + <p> + Use +{@link android.content.ClipData#newPlainText(CharSequence,CharSequence) newPlainText()} + to create a clip from a text string. + </dd> + <dt> +{@link android.content.ClipData#newUri(ContentResolver, CharSequence, Uri) newUri(resolver, label, URI)} + </dt> + <dd> + Returns a {@link android.content.ClipData} object whose single + {@link android.content.ClipData.Item} object contains a URI. The + {@link android.content.ClipDescription} object's label is set to <code>label</code>. + If the URI is a content URI ({@link android.net.Uri#getScheme() Uri.getScheme()} returns + <code>content:</code>), the method uses the {@link android.content.ContentResolver} object + provided in <code>resolver</code> 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 <code>content:</code> URI, the method sets the MIME type to + {@link android.content.ClipDescription#MIMETYPE_TEXT_URILIST}. + <p> + Use +{@link android.content.ClipData#newUri(ContentResolver, CharSequence, Uri) newUri()} + to create a clip from a URI, particularly a <code>content:</code> URI. + </p> + </dd> + <dt> + {@link android.content.ClipData#newIntent(CharSequence, Intent) newIntent(label, intent)} + </dt> + <dd> + Returns a {@link android.content.ClipData} object whose single + {@link android.content.ClipData.Item} object contains an {@link android.content.Intent}. + The {@link android.content.ClipDescription} object's label is set to <code>label</code>. + The MIME type is set to {@link android.content.ClipDescription#MIMETYPE_TEXT_INTENT}. + <p> + Use +{@link android.content.ClipData#newIntent(CharSequence, Intent) newIntent()} + to create a clip from an Intent object. + </dd> +</dl> +<h3 id="CoerceToText">Coercing the clipboard data to text</h3> +<p> + 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()}. +</p> +<p> + 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}: +</p> +<dl> + <dt><em>Text</em></dt> + <dd> + If {@link android.content.ClipData.Item} is text + ({@link android.content.ClipData.Item#getText()} is not null), + {@link android.content.ClipData.Item#coerceToText(Context) coerceToText()} returns the + text. + </dd> + <dt><em>URI</em></dt> + <dd> + If {@link android.content.ClipData.Item} is a URI + ({@link android.content.ClipData.Item#getUri()} is not null), + {@link android.content.ClipData.Item#coerceToText(Context) coerceToText()} tries to use + it as a content URI: + <ul> + <li> + If the URI is a content URI and the provider can return a text stream, + {@link android.content.ClipData.Item#coerceToText(Context) coerceToText()} returns + a text stream. + </li> + <li> + If the URI is a content URI but the provider does not offer a text stream, + {@link android.content.ClipData.Item#coerceToText(Context) coerceToText()} returns + a representation of the URI. The representation is the same as that returned by + {@link android.net.Uri#toString() Uri.toString()}. + </li> + <li> + If the URI is not a content URI, + {@link android.content.ClipData.Item#coerceToText(Context) coerceToText()} returns + a representation of the URI. The representation is the same as that returned by + {@link android.net.Uri#toString() Uri.toString()}. + </li> + </ul> + </dd> + <dt><em>Intent</em></dt> + <dd> + If {@link android.content.ClipData.Item} is an Intent + ({@link android.content.ClipData.Item#getIntent()} is not null), + {@link android.content.ClipData.Item#coerceToText(Context) coerceToText()} converts it to + an Intent URI and returns it. The representation is the same as that returned by + {@link android.content.Intent#toUri(int) Intent.toUri(URI_INTENT_SCHEME)}. + </dd> +</dl> +<p> + 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}. +</p> + <a name="framework"></a> + <img + src="{@docRoot}images/ui/clipboard/copy_paste_framework.png" + alt="A block diagram of the copy and paste framework" height="400px" id="figure1" /> +<p class="img-caption"> + <strong>Figure 1.</strong> The Android clipboard framework +</p> +<h2 id="Copying">Copying to the Clipboard</h2> +<p> + 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: +</p> +<ol> + <li> + If you are copying data using a content URI, set up a content + provider. + <p> + The <a href="{@docRoot}resources/samples/NotePad/index.html"> + Note Pad</a> sample application is an example of using a content provider for + copying and pasting. The +<a href="{@docRoot}resources/samples/NotePad/src/com/example/android/notepad/NotePadProvider.html"> + NotePadProvider</a> class implements the content provider. The +<a href="{@docRoot}resources/samples/NotePad/src/com/example/android/notepad/NotePad.html"> + NotePad</a> class defines a contract between the provider and other applications, + including the supported MIME types. + </p> + </li> + <li> + Get the system clipboard: +<pre> + +... + +// if the user selects copy +case R.id.menu_copy: + +// Gets a handle to the clipboard service. +ClipboardManager clipboard = (ClipboardManager) + getSystemService(Context.CLIPBOARD_SERVICE); +</pre> + </li> + <li> + <p> + Copy the data to a new {@link android.content.ClipData} object: + </p> + <ul> + <li> + <h4>For text</h4> +<pre> +// Creates a new text clip to put on the clipboard +ClipData clip = ClipData.newPlainText("simple text","Hello, World!"); +</pre> + </li> + <li> + <h4>For a URI</h4> + <p> + 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 <a href="#Encoding">Encoding an identifier on the URI</a>: + </p> +<pre> +// 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); +</pre> + </li> + <li> + <h4>For an Intent</h4> + <p> + This snippet constructs an Intent for an application + and then puts it in the clip object: + </p> +<pre> +// 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); +</pre> + </li> + </ul> + </li> + <li> + Put the new clip object on the clipboard: +<pre> +// Set the clipboard's primary clip. +clipboard.setPrimaryClip(clip); +</pre> + </li> +</ol> +<h2 id="Pasting">Pasting from the Clipboard</h2> +<p> + 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. +</p> +<h3 id="PastePlainText">Pasting plain text</h3> +<p> + 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: +</p> +<ol> + <li> + Get the global {@link android.content.ClipboardManager} object using + {@link android.content.Context#getSystemService(String) getSystemService(CLIPBOARD_SERVICE)}. Also + declare a global variable to contain the pasted text: +<pre> +ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + +String pasteData = ""; + +</pre> + </li> + <li> + Next, determine if you should enable or disable the "paste" option in the + current Activity. You should verify that the clipboard contains a clip and that you + can handle the type of data represented by the clip: +<pre> +// 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); + } +} +</pre> + </li> + <li> + Copy the data from the clipboard. This point in the program is only reachable if the + "paste" menu item is enabled, so you can assume that the clipboard contains + plain text. You do not yet know if it contains a text string or a URI that points to plain + text. The following snippet tests this, but it only shows the code for handling plain text: +<pre> +// 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; + } +} +</pre> + </li> +</ol> +<h3 id="PasteContentUri">Pasting data from a content URI</h3> +<p> + 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. +</p> +<p> + 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: +</p> +<ol> + <li> + Declare a global variable to contain the MIME type: +<pre> +// 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" +</pre> + </li> + <li> + Get the global clipboard. Also get a content resolver so you can access the content + provider: +<pre> +// Gets a handle to the Clipboard Manager +ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + +// Gets a content resolver instance +ContentResolver cr = getContentResolver(); +</pre> + </li> + <li> + Get the primary clip from the clipboard, and get its contents as a URI: +<pre> +// 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(); +</pre> + </li> + <li> + Test to see if the URI is a content URI by calling + {@link android.content.ContentResolver#getType(Uri) getType(Uri)}. This method returns + null if <code>Uri</code> does not point to a valid content provider: +<pre> + // If the clipboard contains a URI reference + if (pasteUri != null) { + + // Is this a content URI? + String uriMimeType = cr.getType(pasteUri); +</pre> + </li> + <li> + Test to see if the content provider supports a MIME type that the current application + understands. If it does, call + {@link android.content.ContentResolver#query(Uri, String[], String, String[], String) + ContentResolver.query()} to get the data. The return value is a + {@link android.database.Cursor}: +<pre> + // 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(); + } + } + } +} +</pre> + </li> +</ol> +<h3 id="PasteIntent">Pasting an Intent</h3> +<p> + 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: +</p> +<pre> +// 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 +} +</pre> +<h2 id="Provider">Using Content Providers to Copy Complex Data</h2> +<p> + 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. +</p> +<p> + 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. +</p> +<p> + 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. +</p> +<h3 id="Encoding">Encoding an identifier on the URI</h3> +<p> + 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. +</p> +<p> + 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: +</p> +<pre> +"content://com.example.contacts" +</pre> +<p> + If you want to encode a name onto this URI, you would use the following snippet: +</p> +<pre> +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); +</pre> +<p> + 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: +</p> +<pre> +"content://com.example.contacts"/people +"content://com.example.contacts"/people/detail +"content://com.example.contacts"/people/images +</pre> +<p> + You could add another path that is specific to copy URIs: +</p> +<pre> +"content://com.example.contacts/copying" +</pre> +<p> + You could then detect a "copy" URI by pattern-matching and handle it with code that + is specific for copying and pasting. +</p> +<p> + 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. +</p> +<p> + 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. +</p> +<p> + Getting a single record by ID is used in the + <a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> sample application to + open a note from the notes list. The sample uses the <code>_id</code> field from an SQL + database, but you can have any numeric or character identifier you want. +</p> +<h3 id="Records">Copying data structures</h3> +<p> + 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: +</p> +<ul> + <li> + If you already have a content provider, you can add to its functionality. You may only + need to modify its +{@link android.content.ContentResolver#query(Uri, String[], String, String[], String) query()} + method to handle URIs coming from applications that want to paste data. You will + probably want to modify the method to handle a "copy" URI pattern. + </li> + <li> + If your application maintains an internal database, you may + want to move this database into a content provider to facilitate copying from it. + </li> + <li> + If you are not currently using a database, you can implement a simple content provider + whose sole purpose is to offer data to applications that are pasting from the + clipboard. + </li> +</ul> +<p> +In the content provider, you will want to override at least the following methods: +</p> +<dl> + <dt> +{@link android.content.ContentResolver#query(Uri, String[], String, String[], String) query()} + </dt> + <dd> + Pasting applications will assume that they can get your data by using this method with + the URI you put on the clipboard. To support copying, you should have this method + detect URIs that contain a special "copy" path. Your application can then + create a "copy" URI to put on the clipboard, containing the copy path and + a pointer to the exact record you want to copy. + </dd> + <dt> + {@link android.content.ContentProvider#getType(Uri) getType()} + </dt> + <dd> + This method should return the MIME type or types for the data you intend to copy. The method + {@link android.content.ClipData#newUri(ContentResolver, CharSequence, Uri) newUri()} calls + {@link android.content.ContentProvider#getType(Uri) getType()} in order to put the MIME + types into the new {@link android.content.ClipData} object. + <p> + MIME types for complex data are described in the topic + <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>. + </p> + </dd> +</dl> +<p> + 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. +</p> +<p> + The following snippets demonsrate how to set up your application to copy complex data: +</p> +<ol> + <li> + <p> + 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: + </p> +<pre> +// 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" +</pre> + </li> + <li> + In the Activity from which users copy data, + set up the code to copy data to the clipboard. In response to a copy request, put + the URI on the clipboard: +<pre> +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); +</pre> + </li> + + <li> + <p> + 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: + </p> +<pre> +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); +</pre> + </li> + <li> + <p> + 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: + </p> +<pre> +// 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. + + ... + +} +</pre> + </li> + <li> + <p> + Set up the {@link android.content.ContentProvider#getType(Uri) getType()} method to + return an appropriate MIME type for copied data: + </p> +<pre> +// Sets up your provider's getType() method. +public String getType(Uri uri) { + + ... + + switch (sUriMatcher.match(uri)) { + + case GET_SINGLE_CONTACT: + + return (MIME_TYPE_CONTACT); +</pre> + </li> +</ol> +<p> + The section <a href="#PasteContentUri">Pasting data from a content URI</a> + describes how to get a content URI from the clipboard and use it to get and paste data. +</p> +<h3 id="Streams">Copying data streams</h3> +<p> + You can copy and paste large amounts of text and binary data as streams. The data can have + forms such as the following: +</p> + <ul> + <li> + Files stored on the actual device. + </li> + <li> + Streams from sockets. + </li> + <li> + Large amounts of data stored in a provider's underlying database system. + </li> + </ul> +<p> + 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. +</p> +<p> + To set up your application to copy a data stream with a provider, follow these steps: +</p> +<ol> + <li> + Set up a content URI for the data stream you are putting on the clipboard. Options + for doing this include the following: + <ul> + <li> + Encode an identifier for the data stream onto the URI, + as described in the section + <a href="#Encoding">Encoding an identifier on the URI</a>, and then maintain a + table in your provider that contains identifiers and the corresponding stream name. + </li> + <li> + Encode the stream name directly on the URI. + </li> + <li> + Use a unique URI that always returns the current stream from the provider. If you + use this option, you have to remember to update your provider to point to a + different stream whenever you copy the stream to the clipboard via the URI. + </li> + </ul> + </li> + <li> + Provide a MIME type for each type of data stream you plan to offer. Pasting applications + need this information to determine if they can paste the data on the clipboard. + </li> + <li> + Implement one of the {@link android.content.ContentProvider} methods that returns + a file descriptor for a stream. If you encode identifiers on the content URI, use this + method to determine which stream to open. + </li> + <li> + To copy the data stream to the clipboard, construct the content URI and place it + on the clipboard. + </li> +</ol> +<p> + 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. +</p> +<p> + 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()}: +</p> +<dl> + <dt> +{@link android.content.ContentProvider#openTypedAssetFile(Uri,String,Bundle) openTypedAssetFile()} + </dt> + <dd> + This method should return an asset file descriptor, but only if the provided MIME type is + supported by the provider. The caller (the application doing the pasting) provides a MIME + type pattern. The content provider (of the application that has copied a URI to the + clipboard) returns an {@link android.content.res.AssetFileDescriptor} file handle if it + can provide that MIME type, or throws an exception if it can not. + <p> + This method handles subsections of files. You can use it to read assets that the + content provider has copied to the clipboard. + </p> + </dd> + <dt> + {@link android.content.ContentProvider#openAssetFile(Uri, String) openAssetFile()} + </dt> + <dd> + This method is a more general form of +{@link android.content.ContentProvider#openTypedAssetFile(Uri,String,Bundle) openTypedAssetFile()}. + It does not filter for allowed MIME types, but it can read subsections of files. + </dd> + <dt> + {@link android.content.ContentProvider#openFile(Uri, String) openFile()} + </dt> + <dd> + This is a more general form of + {@link android.content.ContentProvider#openAssetFile(Uri, String) openAssetFile()}. It can't + read subsections of files. + </dd> +</dl> +<p> + 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 <a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> sample + application, in the <code>openTypedAssetFile()</code> method of + <code>NotePadProvider.java</code>. +</p> +<h2 id="DataDesign">Designing Effective Copy/Paste Functionality</h2> +<p> + To design effective copy and paste functionality for your application, remember these + points: +</p> + <ul> + <li> + At any time, there is only one clip on the clipboard. A new copy operation by + any application in the system overwrites the previous clip. Since the user may + navigate away from your application and do a copy before returning, you can't assume + that the clipboard contains the clip that the user previously copied in <em>your</em> + application. + </li> + <li> + The intended purpose of multiple {@link android.content.ClipData.Item} + objects per clip is to support copying and pasting of multiple selections rather than + different forms of reference to a single selection. You usually want all of the + {@link android.content.ClipData.Item} objects in a clip to have the same form, that is, + they should all be simple text, content URI, or {@link android.content.Intent}, but not + a mixture. + </li> + <li> + When you provide data, you can offer different MIME representations. Add the MIME types + you support to the {@link android.content.ClipDescription}, and then + implement the MIME types in your content provider. + </li> + <li> + When you get data from the clipboard, your application is responsible for checking the + available MIME types and then deciding which one, if any, to use. Even if there is a + clip on the clipboard and the user requests a paste, your application is not required + to do the paste. You <em>should</em> do the paste if the MIME type is compatible. You + may choose to coerce the data on the clipboard to text using + {@link android.content.ClipData.Item#coerceToText(Context) coerceToText()} if you + choose. If your application supports more than one of the available MIME types, you can + allow the user to choose which one to use. + </li> + </ul> diff --git a/docs/html/guide/topics/ui/drag-drop.jd b/docs/html/guide/topics/ui/drag-drop.jd new file mode 100644 index 0000000..588b05b --- /dev/null +++ b/docs/html/guide/topics/ui/drag-drop.jd @@ -0,0 +1,995 @@ +page.title=Dragging and Dropping +@jd:body +<div id="qv-wrapper"> + <div id="qv"> + <h2>Quickview</h2> + <ul> + <li> + Allow users to move data within your Activity layout using graphical gestures. + </li> + <li> + Supports operations besides data movement. + </li> + <li> + Only works within a single application. + </li> + <li> + Requires API 11. + </li> + </ul> + <h2>In this document</h2> + <ol> + <li> + <a href="#AboutDragging">Overview</a> + <ol> + <li> + <a href="#DragDropLifecycle">The drag/drop process</a> + </li> + <li> + <a href="#AboutDragListeners">The drag event listener and callback method</a> + </li> + <li> + <a href="#AboutDragEvent">Drag events</a> + </li> + <li> + <a href="#AboutDragShadowBuilder"> + The drag shadow</a> + </li> + </ol> + </li> + <li> + <a href="#DesignDragOperation">Designing a Drag and Drop Operation</a> + <ol> + <li> + <a href="#StartDrag">Starting a drag</a> + </li> + <li> + <a href="#HandleStart">Responding to a drag start</a> + </li> + <li> + <a href="#HandleDuring">Handling events during the drag</a> + </li> + <li> + <a href="#HandleDrop">Responding to a drop</a> + </li> + <li> + <a href="#HandleEnd">Responding to a drag end</a> + </li> + <li> + <a href="#RespondEventSample">Responding to drag events: an example</a> + </li> + </ol> + </li> + </ol> + <h2>Key classes</h2> + <ol> + <li> + {@link android.view.View View} + </li> + <li> + {@link android.view.View.OnLongClickListener OnLongClickListener} + </li> + <li> + {@link android.view.View.OnDragListener OnDragListener} + </li> + <li> + {@link android.view.DragEvent DragEvent} + </li> + <li> + {@link android.view.View.DragShadowBuilder DragShadowBuilder} + </li> + <li> + {@link android.content.ClipData ClipData} + </li> + <li> + {@link android.content.ClipDescription ClipDescription} + </li> + </ol> + <h2>Related Samples</h2> + <ol> + <li> + <a href="{@docRoot}resources/samples/Honeycomb-Gallery/index.html"> + Honeycomb-Gallery</a> sample application. + </li> + <li> + <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/DragAndDropDemo.html"> +DragAndDropDemo.java</a> and + <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/DraggableDot.html"> +DraggableDot.java</a> in <a href="{@docRoot}resources/samples/ApiDemos/index.html">Api Demos</a>. + </li> + </ol> + <h2>See also</h2> + <ol> + <li> + <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> + </li> + <li> + <a href="{@docRoot}guide/topics/ui/ui-events.html">Handling UI Events</a> + </li> + </ol> + </div> +</div> +<p> + 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. +</p> +<p> + 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. +</p> +<h2 id="AboutDragging">Overview</h2> +<p> + 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. +</p> +<p> + 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 + <a href="#AboutDragListeners">The drag event listener and callback method</a>. +</p> +<p class="note"> + <strong>Note</strong>: 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. +</p> +<p> + 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. +</p> +<p> + 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. +</p> +<p> + 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. +</p> +<p> + 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. +</p> +<h3 id="DragDropLifecycle">The drag/drop process</h3> +<p> + There are basically four steps or states in the drag and drop process: +</p> +<dl> + <dt> + <em>Started</em> + </dt> + <dd> + In response to the user's gesture to begin a drag, your application calls + {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()} + to tell the system to start a drag. The arguments + {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()} + provide the data to be dragged, metadata for this data, and a callback for drawing the + drag shadow. + <p> + The system first responds by calling back to your application to get a drag shadow. It + then displays the drag shadow on the device. + </p> + <p> + 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 <code>true</code>. + 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. + </p> + <p> + If the drag event listener returns <code>false</code>, 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 <code>false</code>, the + listener tells the system that it is not interested in the drag operation and + does not want to accept the dragged data. + </p> + </dd> + <dt> + <em>Continuing</em> + </dt> + <dd> + The user continues the drag. As the drag shadow intersects the bounding box of a View + object, the system sends one or more drag events to the View object's drag event + listener (if it is registered to receive events). The listener may choose to + alter its View object's appearance in response to the event. For example, if the event + indicates that the drag shadow has entered the bounding box of the View + (action type {@link android.view.DragEvent#ACTION_DRAG_ENTERED}), the listener + can react by highlighting its View. + </dd> + <dt> + <em>Dropped</em> + </dt> + <dd> + The user releases the drag shadow within the bounding box of a View that can accept the + data. The system sends the View object's listener a drag event with action type + {@link android.view.DragEvent#ACTION_DROP}. The drag event contains the data that was + passed to the system in the call to + {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()} + that started the operation. The listener is expected to return boolean <code>true</code> to + the system if code for accepting the drop succeeds. + <p> + 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. + </p> + </dd> + <dt> + <em>Ended</em> + </dt> + <dd> + After the user releases the drag shadow, and after the system sends out (if necessary) + a drag event with action type {@link android.view.DragEvent#ACTION_DROP}, the system sends + out a drag event with action type {@link android.view.DragEvent#ACTION_DRAG_ENDED} to + indicate that the drag operation is over. This is done regardless of where the user released + the drag shadow. The event is sent to every listener that is registered to receive drag + events, even if the listener received the {@link android.view.DragEvent#ACTION_DROP} event. + </dd> +</dl> +<p> + Each of these four steps is described in more detail in the section + <a href="#DesignDragOperation">Designing a Drag and Drop Operation</a>. +</p> +<h3 id="AboutDragListeners">The drag event listener and callback method</h3> +<p> + 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. +</p> +<p> + 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()}. +</p> +<p> + 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 <code>false</code>. +</p> +<p> + 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. +</p> +<h3 id="AboutDragEvent">Drag events</h3> +<p> + 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. +</p> +<p> + 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 <a href="table1">table 1</a>. +</p> +<p> + 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 <a href="table2">table 2</a>. It is also described in detail with + the event for which it is valid in the section + <a href="#DesignDragOperation">Designing a Drag and Drop Operation</a>. +</p> +<p class="table-caption" id="table1"> + <strong>Table 1.</strong> DragEvent action types +</p> +<table> + <tr> + <th scope="col">getAction() value</th> + <th scope="col">Meaning</th> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DRAG_STARTED}</td> + <td> + 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. + </td> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DRAG_ENTERED}</td> + <td> + 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 + <code>true</code> to the system. + </td> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DRAG_LOCATION}</td> + <td> + 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. + </td> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DRAG_EXITED}</td> + <td> + 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. + </td> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DROP}</td> + <td> + 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 <code>true</code> 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. + <p> + The listener is expected to return boolean <code>true</code> if it successfully + processes the drop. Otherwise, it should return <code>false</code>. + </p> + </td> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DRAG_ENDED}</td> + <td> + 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 <code>false</code>. + </td> + </tr> +</table> +<p class="table-caption" id="table2"> + <strong>Table 2.</strong> Valid DragEvent data by action type</p> +<table> + <tr> + <th scope="col">{@link android.view.DragEvent#getAction()} value</th> + <th scope="col">{@link android.view.DragEvent#getClipDescription()} value</th> + <th scope="col">{@link android.view.DragEvent#getLocalState()} value</th> + <th scope="col">{@link android.view.DragEvent#getX()} value</th> + <th scope="col">{@link android.view.DragEvent#getY()} value</th> + <th scope="col">{@link android.view.DragEvent#getClipData()} value</th> + <th scope="col">{@link android.view.DragEvent#getResult()} value</th> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DRAG_STARTED}</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;"> </td> + <td style="text-align: center;"> </td> + <td style="text-align: center;"> </td> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DRAG_ENTERED}</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;"> </td> + <td style="text-align: center;"> </td> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DRAG_LOCATION}</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;"> </td> + <td style="text-align: center;"> </td> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DRAG_EXITED}</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;"> </td> + <td style="text-align: center;"> </td> + <td style="text-align: center;"> </td> + <td style="text-align: center;"> </td> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DROP}</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;"> </td> + </tr> + <tr> + <td>{@link android.view.DragEvent#ACTION_DRAG_ENDED}</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;">X</td> + <td style="text-align: center;"> </td> + <td style="text-align: center;"> </td> + <td style="text-align: center;"> </td> + <td style="text-align: center;">X</td> + </tr> +</table> +<p> + 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. +</p> +<p> + If a method does not contain valid data for a particular action type, it returns either + <code>null</code> or 0, depending on its result type. +</p> +<h3 id="AboutDragShadowBuilder"> + The drag shadow +</h3> +<p> + 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. +</p> +<p> + 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. +</p> +<p> + The {@link android.view.View.DragShadowBuilder} class has two constructors: +</p> + <dl> + <dt>{@link android.view.View.DragShadowBuilder#View.DragShadowBuilder(View)}</dt> + <dd> + This constructor accepts any of your application's + {@link android.view.View} objects. The constructor stores the View object + in the {@link android.view.View.DragShadowBuilder} object, so during + the callback you can access it as you construct your drag shadow. + It doesn't have to be associated with the View (if any) that the user + selected to start the drag operation. + <p> + 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. + </p> + </dd> + <dt>{@link android.view.View.DragShadowBuilder#View.DragShadowBuilder()}</dt> + <dd> + If you use this constructor, no View object is available in the + {@link android.view.View.DragShadowBuilder} object (the field is set to <code>null</code>). + 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 <em>not</em> give an error. + </dd> +</dl> +<p> + The {@link android.view.View.DragShadowBuilder} class has two methods: +</p> +<dl> + <dt> +{@link android.view.View.DragShadowBuilder#onProvideShadowMetrics(Point,Point) onProvideShadowMetrics()} + </dt> + <dd> + The system calls this method immediately after you call +{@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}. Use it + to send to the system the dimensions and touch point of the drag shadow. The method has two + arguments: + <dl> + <dt><em>dimensions</em></dt> + <dd> + A {@link android.graphics.Point} object. The drag shadow width goes in + {@link android.graphics.Point#x} and its height goes in + {@link android.graphics.Point#y}. + </dd> + <dt><em>touch_point</em></dt> + <dd> + A {@link android.graphics.Point} object. The touch point is the location within the + drag shadow that should be under the user's finger during the drag. Its X + position goes in {@link android.graphics.Point#x} and its Y position goes in + {@link android.graphics.Point#y} + </dd> + </dl> + </dd> + <dt> + {@link android.view.View.DragShadowBuilder#onDrawShadow(Canvas) onDrawShadow()} + </dt> + <dd> + Immediately after the call to +{@link android.view.View.DragShadowBuilder#onProvideShadowMetrics(Point,Point) onProvideShadowMetrics()} + the system calls + {@link android.view.View.DragShadowBuilder#onDrawShadow(Canvas) onDrawShadow()} to get the + drag shadow itself. The method has a single argument, a {@link android.graphics.Canvas} + object that the system constructs from the parameters you provide in +{@link android.view.View.DragShadowBuilder#onProvideShadowMetrics(Point,Point) onProvideShadowMetrics()} + Use it to draw the drag shadow in the provided {@link android.graphics.Canvas} object. + </dd> +</dl> +<p> + 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. +</p> +<h2 id="DesignDragOperation">Designing a Drag and Drop Operation</h2> +<p> + 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. +</p> +<h3 id="StartDrag">Starting a drag</h3> +<p> + The user starts a drag with a drag gesture, usually a long press, on a View object. + In response, you should do the following: +</p> +<ol> + <li> + As necessary, create a {@link android.content.ClipData} and + {@link android.content.ClipData.Item} for the data being moved. As part of the + ClipData object, supply metadata that is stored in a {@link android.content.ClipDescription} + object within the ClipData. For a drag and drop operation that does not represent data + movement, you may want to use <code>null</code> instead of an actual object. + <p> + 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}: + </p> +<pre> +// 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) + ); + + } +} +</pre> + </li> + <li> + The following code snippet defines {@code myDragShadowBuilder} + It creates a drag shadow for dragging a TextView as a small gray rectangle: +<pre> + 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); + } + } +</pre> + <p class="note"> + <strong>Note:</strong> 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. + </p> + </li> +</ol> +<h3 id="HandleStart">Responding to a drag start</h3> +<p> + 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}. +</p> +<p> + In response to an event with the action type {@link android.view.DragEvent#ACTION_DRAG_STARTED}, + a listener should do the following: +</p> +<ol> + <li> + Call {@link android.view.DragEvent#getClipDescription()} to get the + {@link android.content.ClipDescription}. Use the MIME type methods in + {@link android.content.ClipDescription} to see if the listener can accept the data being + dragged. + <p> + If the drag and drop operation does not represent data movement, this may not be + necessary. + </p> + </li> + <li> + If the listener can accept a drop, it should return <code>true</code>. This tells + the system to continue to send drag events to the listener. + If it can't accept a drop, it should return <code>false</code>, and the system + will stop sending drag events until it sends out + {@link android.view.DragEvent#ACTION_DRAG_ENDED}. + </li> +</ol> +<p> + 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()}. +</p> +<h3 id="HandleDuring">Handling events during the drag</h3> +<p> + During the drag, listeners that returned <code>true</code> 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. +</p> +<p> + During the drag, listeners primarily use drag events to decide if they should change the + appearance of their View. +</p> +<p> + During the drag, {@link android.view.DragEvent#getAction()} returns one of three + values: +</p> +<ul> + <li> + {@link android.view.DragEvent#ACTION_DRAG_ENTERED}: + The listener receives this when the touch point + (the point on the screen underneath the user's finger) has entered the bounding box of the + listener's View. + </li> + <li> + {@link android.view.DragEvent#ACTION_DRAG_LOCATION}: Once the listener receives an + {@link android.view.DragEvent#ACTION_DRAG_ENTERED} event, and before it receives an + A{@link android.view.DragEvent#ACTION_DRAG_EXITED} event, it receives a new + {@link android.view.DragEvent#ACTION_DRAG_LOCATION} event every time the touch point moves. + The {@link android.view.DragEvent#getX()} and {@link android.view.DragEvent#getY()} methods + return the the X and Y coordinates of the touch point. + </li> + <li> + {@link android.view.DragEvent#ACTION_DRAG_EXITED}: This event is sent to a listener that + previously received {@link android.view.DragEvent#ACTION_DRAG_ENTERED}, after + the drag shadow is no longer within the bounding box of the listener's View. + </li> +</ul> +<p> + 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: +</p> +<ul> + <li> + In response to {@link android.view.DragEvent#ACTION_DRAG_ENTERED} or + {@link android.view.DragEvent#ACTION_DRAG_LOCATION}, the listener can change the appearance + of the View to indicate that it is about to receive a drop. + </li> + <li> + An event with the action type {@link android.view.DragEvent#ACTION_DRAG_LOCATION} contains + valid data for {@link android.view.DragEvent#getX()} and + {@link android.view.DragEvent#getY()}, corresponding to the location of the touch point. + The listener may want to use this information to alter the appearance of that part of the + View that is at the touch point. The listener can also use this information + to determine the exact position where the user is going to drop the drag shadow. + </li> + <li> + In response to {@link android.view.DragEvent#ACTION_DRAG_EXITED}, the listener should reset + any appearance changes it applied in response to + {@link android.view.DragEvent#ACTION_DRAG_ENTERED} or + {@link android.view.DragEvent#ACTION_DRAG_LOCATION}. This indicates to the user that + the View is no longer an imminent drop target. + </li> +</ul> +<h3 id="HandleDrop">Responding to a drop</h3> +<p> + 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: +</p> +<ol> + <li> + Call {@link android.view.DragEvent#getClipData()} to get the + {@link android.content.ClipData} object that was originally supplied in the call + to +{@link android.view.View#startDrag(ClipData, View.DragShadowBuilder, Object, int) startDrag()} + and store it. If the drag and drop operation does not represent data movement, + this may not be necessary. + </li> + <li> + Return boolean <code>true</code> to indicate that the drop was processed successfully, or + boolean <code>false</code> 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. + <p> + 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 <code>false</code>. + </p> + </li> +</ol> +<p> + 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. +</p> +<p> + 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. +</p> +<h3 id="HandleEnd">Responding to a drag end</h3> +<p> + 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. +</p> +<p> + Each listener should do the following: +</p> +<ol> + <li> + If listener changed its View object's appearance during the operation, it should reset the + View to its default appearance. This is a visual indication to the user that the operation + is over. + </li> + <li> + The listener can optionally call {@link android.view.DragEvent#getResult()} to find out more + about the operation. If a listener returned <code>true</code> in response to an event of + action type {@link android.view.DragEvent#ACTION_DROP}, then + {@link android.view.DragEvent#getResult()} will return boolean <code>true</code>. In all + other cases, {@link android.view.DragEvent#getResult()} returns boolean <code>false</code>, + including any case in which the system did not send out a + {@link android.view.DragEvent#ACTION_DROP} event. + </li> + <li> + The listener should return boolean <code>true</code> to the system. + </li> +</ol> +<p> +</p> +<h3 id="RespondEventSample">Responding to drag events: an example</h3> +<p> + 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: +</p> +<pre> +// 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; + }; + }; +}; +</pre>
\ 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 Binary files differnew file mode 100755 index 0000000..57facaa --- /dev/null +++ b/docs/html/images/ui/clipboard/copy_paste_framework.png |
