1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
|
page.title=Drag and Drop
parent.title=User Interface
parent.link=index.html
@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/HoneycombGallery/index.html">
Honeycomb Gallery</a>.
</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">Input 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.DragShadowBuilder 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>
|