DragEvent.java revision 32736f085b74c4dcc9da61212ccbd6fe2de193a7
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.view; 18 19import android.content.ClipData; 20import android.content.ClipDescription; 21import android.os.Parcel; 22import android.os.Parcelable; 23 24//TODO: Improve Javadoc 25/** 26 * Represents an event that is sent out by the system at various times during a drag and drop 27 * operation. It is a complex data structure that contains several important pieces of data about 28 * the operation and the underlying data. 29 * <p> 30 * View objects that receive a DragEvent call {@link #getAction()}, which returns 31 * an action type that indicates the state of the drag and drop operation. This allows a View 32 * object to react to a change in state by changing its appearance or performing other actions. 33 * For example, a View can react to the {@link #ACTION_DRAG_ENTERED} action type by 34 * by changing one or more colors in its displayed image. 35 * </p> 36 * <p> 37 * During a drag and drop operation, the system displays an image that the user drags. This image 38 * is called a drag shadow. Several action types reflect the position of the drag shadow relative 39 * to the View receiving the event. 40 * </p> 41 * <p> 42 * Most methods return valid data only for certain event actions. This is summarized in the 43 * following table. Each possible {@link #getAction()} value is listed in the first column. The 44 * other columns indicate which method or methods return valid data for that getAction() value: 45 * </p> 46 * <table> 47 * <tr> 48 * <th scope="col">getAction() Value</th> 49 * <th scope="col">getClipDescription()</th> 50 * <th scope="col">getLocalState()</th> 51 * <th scope="col">getX()</th> 52 * <th scope="col">getY()</th> 53 * <th scope="col">getClipData()</th> 54 * <th scope="col">getResult()</th> 55 * </tr> 56 * <tr> 57 * <td>ACTION_DRAG_STARTED</td> 58 * <td style="text-align: center;">X</td> 59 * <td style="text-align: center;">X</td> 60 * <td style="text-align: center;">X</td> 61 * <td style="text-align: center;">X</td> 62 * <td style="text-align: center;"> </td> 63 * <td style="text-align: center;"> </td> 64 * </tr> 65 * <tr> 66 * <td>ACTION_DRAG_ENTERED</td> 67 * <td style="text-align: center;">X</td> 68 * <td style="text-align: center;">X</td> 69 * <td style="text-align: center;"> </td> 70 * <td style="text-align: center;"> </td> 71 * <td style="text-align: center;"> </td> 72 * <td style="text-align: center;"> </td> 73 * </tr> 74 * <tr> 75 * <td>ACTION_DRAG_LOCATION</td> 76 * <td style="text-align: center;">X</td> 77 * <td style="text-align: center;">X</td> 78 * <td style="text-align: center;">X</td> 79 * <td style="text-align: center;">X</td> 80 * <td style="text-align: center;"> </td> 81 * <td style="text-align: center;"> </td> 82 * </tr> 83 * <tr> 84 * <td>ACTION_DRAG_EXITED</td> 85 * <td style="text-align: center;">X</td> 86 * <td style="text-align: center;">X</td> 87 * <td style="text-align: center;"> </td> 88 * <td style="text-align: center;"> </td> 89 * <td style="text-align: center;"> </td> 90 * <td style="text-align: center;"> </td> 91 * </tr> 92 * <tr> 93 * <td>ACTION_DROP</td> 94 * <td style="text-align: center;">X</td> 95 * <td style="text-align: center;">X</td> 96 * <td style="text-align: center;">X</td> 97 * <td style="text-align: center;">X</td> 98 * <td style="text-align: center;">X</td> 99 * <td style="text-align: center;"> </td> 100 * </tr> 101 * <tr> 102 * <td>ACTION_DRAG_ENDED</td> 103 * <td style="text-align: center;">X</td> 104 * <td style="text-align: center;">X</td> 105 * <td style="text-align: center;"> </td> 106 * <td style="text-align: center;"> </td> 107 * <td style="text-align: center;"> </td> 108 * <td style="text-align: center;">X</td> 109 * </tr> 110 * </table> 111 * <p> 112 * The {@link android.view.DragEvent#getAction()}, 113 * {@link android.view.DragEvent#describeContents()}, 114 * {@link android.view.DragEvent#writeToParcel(Parcel,int)}, and 115 * {@link android.view.DragEvent#toString()} methods always return valid data. 116 * </p> 117 */ 118public class DragEvent implements Parcelable { 119 private static final boolean TRACK_RECYCLED_LOCATION = false; 120 121 int mAction; 122 float mX, mY; 123 ClipDescription mClipDescription; 124 ClipData mClipData; 125 Object mLocalState; 126 boolean mDragResult; 127 128 private DragEvent mNext; 129 private RuntimeException mRecycledLocation; 130 private boolean mRecycled; 131 132 private static final int MAX_RECYCLED = 10; 133 private static final Object gRecyclerLock = new Object(); 134 private static int gRecyclerUsed = 0; 135 private static DragEvent gRecyclerTop = null; 136 137 /** 138 * Action constant returned by {@link #getAction()}: Signals the start of a 139 * drag and drop operation. The View should return {@code true} from its 140 * {@link View#onDragEvent(DragEvent) onDragEvent()} handler method or 141 * {@link View.View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} listener 142 * if it can accept a drop. The onDragEvent() or onDrag() methods usually inspect the metadata 143 * from {@link #getClipDescription()} to determine if they can accept the data contained in 144 * this drag. For an operation that doesn't represent data transfer, these methods may 145 * perform other actions to determine whether or not the View should accept the drag. 146 * If the View wants to indicate that it is a valid drop target, it can also react by 147 * changing its appearance. 148 * <p> 149 * A View only receives further drag events if it returns {@code true} in response to 150 * ACTION_DRAG_STARTED. 151 * </p> 152 * @see #ACTION_DRAG_ENDED 153 */ 154 public static final int ACTION_DRAG_STARTED = 1; 155 156 /** 157 * Action constant returned by {@link #getAction()}: Sent to a View after 158 * {@link #ACTION_DRAG_ENTERED} if the drag shadow is still within the View object's bounding 159 * box. The {@link #getX()} and {@link #getY()} methods supply 160 * the X and Y position of of the drag point within the View object's bounding box. 161 * <p> 162 * A View receives an {@link #ACTION_DRAG_ENTERED} event before receiving any 163 * ACTION_DRAG_LOCATION events. 164 * </p> 165 * <p> 166 * The system stops sending ACTION_DRAG_LOCATION events to a View once the user moves the 167 * drag shadow out of the View object's bounding box. If the user moves the drag shadow back 168 * into the View object's bounding box, the View receives an ACTION_DRAG_ENTERED again before 169 * receiving any more ACTION_DRAG_LOCATION events. 170 * </p> 171 * @see #ACTION_DRAG_ENTERED 172 * @see #getX() 173 * @see #getY() 174 */ 175 public static final int ACTION_DRAG_LOCATION = 2; 176 177 /** 178 * Action constant returned by {@link #getAction()}: Signals to a View that the user 179 * has released the drag shadow, and the drag point is within the bounding box of the View. 180 * The View should retrieve the data from the DragEvent by calling {@link #getClipData()}. 181 * The methods {@link #getX()} and {@link #getY()} return the X and Y position of the drop point 182 * within the View object's bounding box. 183 * <p> 184 * The View should return {@code true} from its {@link View#onDragEvent(DragEvent)} 185 * handler or {@link View.View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} 186 * listener if it accepted the drop, and {@code false} if it ignored the drop. 187 * </p> 188 * <p> 189 * The View can also react to this action by changing its appearance. 190 * </p> 191 * @see #getClipData() 192 * @see #getX() 193 * @see #getY() 194 */ 195 public static final int ACTION_DROP = 3; 196 197 /** 198 * Action constant returned by {@link #getAction()}: Signals to a View that the drag and drop 199 * operation has concluded. A View that changed its appearance during the operation should 200 * return to its usual drawing state in response to this event. 201 * <p> 202 * All views that received an ACTION_DRAG_STARTED event will receive the 203 * ACTION_DRAG_ENDED event even if they are not currently visible when the drag ends. 204 * </p> 205 * <p> 206 * The View object can call {@link #getResult()} to see the result of the operation. 207 * If a View returned {@code true} in response to {@link #ACTION_DROP}, then 208 * getResult() returns {@code true}, otherwise it returns {@code false}. 209 * </p> 210 * @see #ACTION_DRAG_STARTED 211 * @see #getResult() 212 */ 213 public static final int ACTION_DRAG_ENDED = 4; 214 215 /** 216 * Action constant returned by {@link #getAction()}: Signals to a View that the drag point has 217 * entered the bounding box of the View. 218 * <p> 219 * If the View can accept a drop, it can react to ACTION_DRAG_ENTERED 220 * by changing its appearance in a way that tells the user that the View is the current 221 * drop target. 222 * </p> 223 * The system stops sending ACTION_DRAG_LOCATION events to a View once the user moves the 224 * drag shadow out of the View object's bounding box. If the user moves the drag shadow back 225 * into the View object's bounding box, the View receives an ACTION_DRAG_ENTERED again before 226 * receiving any more ACTION_DRAG_LOCATION events. 227 * </p> 228 * @see #ACTION_DRAG_ENTERED 229 * @see #ACTION_DRAG_LOCATION 230 */ 231 public static final int ACTION_DRAG_ENTERED = 5; 232 233 /** 234 * Action constant returned by {@link #getAction()}: Signals that the user has moved the 235 * drag shadow outside the bounding box of the View. 236 * The View can react by changing its appearance in a way that tells the user that 237 * View is no longer the immediate drop target. 238 * <p> 239 * After the system sends an ACTION_DRAG_EXITED event to the View, the View receives no more 240 * ACTION_DRAG_LOCATION events until the user drags the drag shadow back over the View. 241 * </p> 242 * 243 */ 244 public static final int ACTION_DRAG_EXITED = 6; 245 246 private DragEvent() { 247 } 248 249 private void init(int action, float x, float y, ClipDescription description, ClipData data, 250 Object localState, boolean result) { 251 mAction = action; 252 mX = x; 253 mY = y; 254 mClipDescription = description; 255 mClipData = data; 256 mLocalState = localState; 257 mDragResult = result; 258 } 259 260 static DragEvent obtain() { 261 return DragEvent.obtain(0, 0f, 0f, null, null, null, false); 262 } 263 264 /** @hide */ 265 public static DragEvent obtain(int action, float x, float y, Object localState, 266 ClipDescription description, ClipData data, boolean result) { 267 final DragEvent ev; 268 synchronized (gRecyclerLock) { 269 if (gRecyclerTop == null) { 270 ev = new DragEvent(); 271 ev.init(action, x, y, description, data, localState, result); 272 return ev; 273 } 274 ev = gRecyclerTop; 275 gRecyclerTop = ev.mNext; 276 gRecyclerUsed -= 1; 277 } 278 ev.mRecycledLocation = null; 279 ev.mRecycled = false; 280 ev.mNext = null; 281 282 ev.init(action, x, y, description, data, localState, result); 283 284 return ev; 285 } 286 287 /** @hide */ 288 public static DragEvent obtain(DragEvent source) { 289 return obtain(source.mAction, source.mX, source.mY, source.mLocalState, 290 source.mClipDescription, source.mClipData, source.mDragResult); 291 } 292 293 /** 294 * Inspect the action value of this event. 295 * @return One of the following action constants, in the order in which they usually occur 296 * during a drag and drop operation: 297 * <ul> 298 * <li>{@link #ACTION_DRAG_STARTED}</li> 299 * <li>{@link #ACTION_DRAG_ENTERED}</li> 300 * <li>{@link #ACTION_DRAG_LOCATION}</li> 301 * <li>{@link #ACTION_DROP}</li> 302 * <li>{@link #ACTION_DRAG_EXITED}</li> 303 * <li>{@link #ACTION_DRAG_ENDED}</li> 304 * </ul> 305 */ 306 public int getAction() { 307 return mAction; 308 } 309 310 /** 311 * Gets the X coordinate of the drag point. The value is only valid if the event action is 312 * {@link #ACTION_DRAG_LOCATION} or {@link #ACTION_DROP}. 313 * @return The current drag point's Y coordinate 314 */ 315 public float getX() { 316 return mX; 317 } 318 319 /** 320 * Gets the Y coordinate of the drag point. The value is valid if the 321 * event action is {@link #ACTION_DRAG_ENTERED}, {@link #ACTION_DRAG_LOCATION}, 322 * {@link #ACTION_DROP}, or {@link #ACTION_DRAG_EXITED}. 323 * @return The current drag point's Y coordinate 324 */ 325 public float getY() { 326 return mY; 327 } 328 329 /** 330 * Returns the {@link android.content.ClipData} object sent to the system as part of the call 331 * to 332 * {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}. 333 * This method only returns valid data if the event action is {@link #ACTION_DROP}. 334 * @return The ClipData sent to the system by startDrag(). 335 */ 336 public ClipData getClipData() { 337 return mClipData; 338 } 339 340 /** 341 * Returns the {@link android.content.ClipDescription} object contained in the 342 * {@link android.content.ClipData} object sent to the system as part of the call to 343 * {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}. 344 * The drag handler or listener for a View can use the metadata in this object to decide if the 345 * View can accept the dragged View object's data. 346 * <p> 347 * This method returns valid data for all event actions. 348 * @return The ClipDescription that was part of the ClipData sent to the system by startDrag(). 349 */ 350 public ClipDescription getClipDescription() { 351 return mClipDescription; 352 } 353 354 /** 355 * Returns the local state object sent to the system as part of the call to 356 * {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}. 357 * The object is intended to provide local information about the drag and drop operation. For 358 * example, it can indicate whether the drag and drop operation is a copy or a move. 359 * <p> 360 * This method returns valid data for all event actions. 361 * </p> 362 * @return The local state object sent to the system by startDrag(). 363 */ 364 public Object getLocalState() { 365 return mLocalState; 366 } 367 368 /** 369 * <p> 370 * Returns an indication of the result of the drag and drop operation. 371 * This method only returns valid data if the action type is {@link #ACTION_DRAG_ENDED}. 372 * The return value depends on what happens after the user releases the drag shadow. 373 * </p> 374 * <p> 375 * If the user releases the drag shadow on a View that can accept a drop, the system sends an 376 * {@link #ACTION_DROP} event to the View object's drag event listener. If the listener 377 * returns {@code true}, then getResult() will return {@code true}. 378 * If the listener returns {@code false}, then getResult() returns {@code false}. 379 * </p> 380 * <p> 381 * Notice that getResult() also returns {@code false} if no {@link #ACTION_DROP} is sent. This 382 * happens, for example, when the user releases the drag shadow over an area outside of the 383 * application. In this case, the system sends out {@link #ACTION_DRAG_ENDED} for the current 384 * operation, but never sends out {@link #ACTION_DROP}. 385 * </p> 386 * @return {@code true} if a drag event listener returned {@code true} in response to 387 * {@link #ACTION_DROP}. If the system did not send {@link #ACTION_DROP} before 388 * {@link #ACTION_DRAG_ENDED}, or if the listener returned {@code false} in response to 389 * {@link #ACTION_DROP}, then {@code false} is returned. 390 */ 391 public boolean getResult() { 392 return mDragResult; 393 } 394 395 /** 396 * Recycle the DragEvent, to be re-used by a later caller. After calling 397 * this function you must never touch the event again. 398 * 399 * @hide 400 */ 401 public final void recycle() { 402 // Ensure recycle is only called once! 403 if (TRACK_RECYCLED_LOCATION) { 404 if (mRecycledLocation != null) { 405 throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation); 406 } 407 mRecycledLocation = new RuntimeException("Last recycled here"); 408 } else { 409 if (mRecycled) { 410 throw new RuntimeException(toString() + " recycled twice!"); 411 } 412 mRecycled = true; 413 } 414 415 mClipData = null; 416 mClipDescription = null; 417 mLocalState = null; 418 419 synchronized (gRecyclerLock) { 420 if (gRecyclerUsed < MAX_RECYCLED) { 421 gRecyclerUsed++; 422 mNext = gRecyclerTop; 423 gRecyclerTop = this; 424 } 425 } 426 } 427 428 /** 429 * Returns a string containing a concise, human-readable representation of this DragEvent 430 * object. 431 * @return A string representation of the DragEvent object. 432 */ 433 @Override 434 public String toString() { 435 return "DragEvent{" + Integer.toHexString(System.identityHashCode(this)) 436 + " action=" + mAction + " @ (" + mX + ", " + mY + ") desc=" + mClipDescription 437 + " data=" + mClipData + " local=" + mLocalState + " result=" + mDragResult 438 + "}"; 439 } 440 441 /* Parcelable interface */ 442 443 /** 444 * Returns information about the {@link android.os.Parcel} representation of this DragEvent 445 * object. 446 * @return Information about the {@link android.os.Parcel} representation. 447 */ 448 public int describeContents() { 449 return 0; 450 } 451 452 /** 453 * Creates a {@link android.os.Parcel} object from this DragEvent object. 454 * @param dest A {@link android.os.Parcel} object in which to put the DragEvent object. 455 * @param flags Flags to store in the Parcel. 456 */ 457 public void writeToParcel(Parcel dest, int flags) { 458 dest.writeInt(mAction); 459 dest.writeFloat(mX); 460 dest.writeFloat(mY); 461 dest.writeInt(mDragResult ? 1 : 0); 462 if (mClipData == null) { 463 dest.writeInt(0); 464 } else { 465 dest.writeInt(1); 466 mClipData.writeToParcel(dest, flags); 467 } 468 if (mClipDescription == null) { 469 dest.writeInt(0); 470 } else { 471 dest.writeInt(1); 472 mClipDescription.writeToParcel(dest, flags); 473 } 474 } 475 476 /** 477 * A container for creating a DragEvent from a Parcel. 478 */ 479 public static final Parcelable.Creator<DragEvent> CREATOR = 480 new Parcelable.Creator<DragEvent>() { 481 public DragEvent createFromParcel(Parcel in) { 482 DragEvent event = DragEvent.obtain(); 483 event.mAction = in.readInt(); 484 event.mX = in.readFloat(); 485 event.mY = in.readFloat(); 486 event.mDragResult = (in.readInt() != 0); 487 if (in.readInt() != 0) { 488 event.mClipData = ClipData.CREATOR.createFromParcel(in); 489 } 490 if (in.readInt() != 0) { 491 event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in); 492 } 493 return event; 494 } 495 496 public DragEvent[] newArray(int size) { 497 return new DragEvent[size]; 498 } 499 }; 500} 501