DragController.java revision a34abf8c781485b788fddacb352d586bffca886c
1/* 2 * Copyright (C) 2008 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 com.android.launcher2; 18 19import com.android.launcher.R; 20 21import android.content.Context; 22import android.graphics.Bitmap; 23import android.graphics.PointF; 24import android.graphics.Rect; 25import android.graphics.RectF; 26import android.os.Handler; 27import android.os.IBinder; 28import android.os.Vibrator; 29import android.util.DisplayMetrics; 30import android.util.Log; 31import android.view.KeyEvent; 32import android.view.MotionEvent; 33import android.view.View; 34import android.view.ViewConfiguration; 35import android.view.WindowManager; 36import android.view.inputmethod.InputMethodManager; 37 38import java.util.ArrayList; 39 40/** 41 * Class for initiating a drag within a view or across multiple views. 42 */ 43public class DragController { 44 @SuppressWarnings({"UnusedDeclaration"}) 45 private static final String TAG = "Launcher.DragController"; 46 47 /** Indicates the drag is a move. */ 48 public static int DRAG_ACTION_MOVE = 0; 49 50 /** Indicates the drag is a copy. */ 51 public static int DRAG_ACTION_COPY = 1; 52 53 private static final int SCROLL_DELAY = 600; 54 private static final int VIBRATE_DURATION = 35; 55 56 private static final boolean PROFILE_DRAWING_DURING_DRAG = false; 57 58 private static final int SCROLL_OUTSIDE_ZONE = 0; 59 private static final int SCROLL_WAITING_IN_ZONE = 1; 60 61 static final int SCROLL_LEFT = 0; 62 static final int SCROLL_RIGHT = 1; 63 64 private Context mContext; 65 private Handler mHandler; 66 private final Vibrator mVibrator = new Vibrator(); 67 68 // temporaries to avoid gc thrash 69 private Rect mRectTemp = new Rect(); 70 private final int[] mCoordinatesTemp = new int[2]; 71 72 /** Whether or not we're dragging. */ 73 private boolean mDragging; 74 75 /** X coordinate of the down event. */ 76 private float mMotionDownX; 77 78 /** Y coordinate of the down event. */ 79 private float mMotionDownY; 80 81 /** Info about the screen for clamping. */ 82 private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); 83 84 /** Original view that is being dragged. */ 85 private View mOriginator; 86 87 /** X offset from the upper-left corner of the cell to where we touched. */ 88 private float mTouchOffsetX; 89 90 /** Y offset from the upper-left corner of the cell to where we touched. */ 91 private float mTouchOffsetY; 92 93 /** the area at the edge of the screen that makes the workspace go left 94 * or right while you're dragging. 95 */ 96 private int mScrollZone; 97 98 /** Where the drag originated */ 99 private DragSource mDragSource; 100 101 /** The data associated with the object being dragged */ 102 private Object mDragInfo; 103 104 /** The view that moves around while you drag. */ 105 private DragView mDragView; 106 107 /** Who can receive drop events */ 108 private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>(); 109 110 private ArrayList<DragListener> mListeners = new ArrayList<DragListener>(); 111 112 /** The window token used as the parent for the DragView. */ 113 private IBinder mWindowToken; 114 115 /** The view that will be scrolled when dragging to the left and right edges of the screen. */ 116 private View mScrollView; 117 118 private View mMoveTarget; 119 120 private DragScroller mDragScroller; 121 private int mScrollState = SCROLL_OUTSIDE_ZONE; 122 private ScrollRunnable mScrollRunnable = new ScrollRunnable(); 123 124 private RectF mDeleteRegion; 125 private DropTarget mLastDropTarget; 126 127 private InputMethodManager mInputMethodManager; 128 129 private int mLastTouch[] = new int[2]; 130 private int mDistanceSinceScroll = 0; 131 132 /** 133 * Interface to receive notifications when a drag starts or stops 134 */ 135 interface DragListener { 136 137 /** 138 * A drag has begun 139 * 140 * @param source An object representing where the drag originated 141 * @param info The data associated with the object that is being dragged 142 * @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE} 143 * or {@link DragController#DRAG_ACTION_COPY} 144 */ 145 void onDragStart(DragSource source, Object info, int dragAction); 146 147 /** 148 * The drag has ended 149 */ 150 void onDragEnd(); 151 } 152 153 /** 154 * Used to create a new DragLayer from XML. 155 * 156 * @param context The application's context. 157 */ 158 public DragController(Context context) { 159 mContext = context; 160 mHandler = new Handler(); 161 mScrollZone = context.getResources().getDimensionPixelSize(R.dimen.scroll_zone); 162 } 163 164 public boolean dragging() { 165 return mDragging; 166 } 167 168 /** 169 * Starts a drag. 170 * 171 * @param v The view that is being dragged 172 * @param source An object representing where the drag originated 173 * @param dragInfo The data associated with the object that is being dragged 174 * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or 175 * {@link #DRAG_ACTION_COPY} 176 */ 177 public void startDrag(View v, DragSource source, Object dragInfo, int dragAction) { 178 startDrag(v, source, dragInfo, dragAction, null); 179 } 180 181 /** 182 * Starts a drag. 183 * 184 * @param v The view that is being dragged 185 * @param source An object representing where the drag originated 186 * @param dragInfo The data associated with the object that is being dragged 187 * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or 188 * {@link #DRAG_ACTION_COPY} 189 * @param dragRegion Coordinates within the bitmap b for the position of item being dragged. 190 * Makes dragging feel more precise, e.g. you can clip out a transparent border 191 */ 192 public void startDrag(View v, DragSource source, Object dragInfo, int dragAction, 193 Rect dragRegion) { 194 mOriginator = v; 195 196 Bitmap b = getViewBitmap(v); 197 198 if (b == null) { 199 // out of memory? 200 return; 201 } 202 203 int[] loc = mCoordinatesTemp; 204 v.getLocationOnScreen(loc); 205 int screenX = loc[0]; 206 int screenY = loc[1]; 207 208 startDrag(b, screenX, screenY, 0, 0, b.getWidth(), b.getHeight(), 209 source, dragInfo, dragAction, dragRegion); 210 211 b.recycle(); 212 213 if (dragAction == DRAG_ACTION_MOVE) { 214 v.setVisibility(View.GONE); 215 } 216 } 217 218 /** 219 * Starts a drag. 220 * 221 * @param v The view that is being dragged 222 * @param bmp The bitmap that represents the view being dragged 223 * @param source An object representing where the drag originated 224 * @param dragInfo The data associated with the object that is being dragged 225 * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or 226 * {@link #DRAG_ACTION_COPY} 227 * @param dragRegion Coordinates within the bitmap b for the position of item being dragged. 228 * Makes dragging feel more precise, e.g. you can clip out a transparent border 229 */ 230 public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo, int dragAction, 231 Rect dragRegion) { 232 mOriginator = v; 233 234 int[] loc = mCoordinatesTemp; 235 v.getLocationOnScreen(loc); 236 int screenX = loc[0]; 237 int screenY = loc[1]; 238 239 startDrag(bmp, screenX, screenY, 0, 0, bmp.getWidth(), bmp.getHeight(), 240 source, dragInfo, dragAction, dragRegion); 241 242 if (dragAction == DRAG_ACTION_MOVE) { 243 v.setVisibility(View.GONE); 244 } 245 } 246 247 /** 248 * Starts a drag. 249 * 250 * @param b The bitmap to display as the drag image. It will be re-scaled to the 251 * enlarged size. 252 * @param screenX The x position on screen of the left-top of the bitmap. 253 * @param screenY The y position on screen of the left-top of the bitmap. 254 * @param textureLeft The left edge of the region inside b to use. 255 * @param textureTop The top edge of the region inside b to use. 256 * @param textureWidth The width of the region inside b to use. 257 * @param textureHeight The height of the region inside b to use. 258 * @param source An object representing where the drag originated 259 * @param dragInfo The data associated with the object that is being dragged 260 * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or 261 * {@link #DRAG_ACTION_COPY} 262 */ 263 public void startDrag(Bitmap b, int screenX, int screenY, 264 int textureLeft, int textureTop, int textureWidth, int textureHeight, 265 DragSource source, Object dragInfo, int dragAction) { 266 startDrag(b, screenX, screenY, textureLeft, textureTop, textureWidth, textureHeight, 267 source, dragInfo, dragAction, null); 268 } 269 270 /** 271 * Starts a drag. 272 * 273 * @param b The bitmap to display as the drag image. It will be re-scaled to the 274 * enlarged size. 275 * @param screenX The x position on screen of the left-top of the bitmap. 276 * @param screenY The y position on screen of the left-top of the bitmap. 277 * @param textureLeft The left edge of the region inside b to use. 278 * @param textureTop The top edge of the region inside b to use. 279 * @param textureWidth The width of the region inside b to use. 280 * @param textureHeight The height of the region inside b to use. 281 * @param source An object representing where the drag originated 282 * @param dragInfo The data associated with the object that is being dragged 283 * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or 284 * {@link #DRAG_ACTION_COPY} 285 * @param dragRegion Coordinates within the bitmap b for the position of item being dragged. 286 * Makes dragging feel more precise, e.g. you can clip out a transparent border 287 */ 288 public void startDrag(Bitmap b, int screenX, int screenY, 289 int textureLeft, int textureTop, int textureWidth, int textureHeight, 290 DragSource source, Object dragInfo, int dragAction, Rect dragRegion) { 291 if (PROFILE_DRAWING_DURING_DRAG) { 292 android.os.Debug.startMethodTracing("Launcher"); 293 } 294 295 // Hide soft keyboard, if visible 296 if (mInputMethodManager == null) { 297 mInputMethodManager = (InputMethodManager) 298 mContext.getSystemService(Context.INPUT_METHOD_SERVICE); 299 } 300 mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0); 301 302 for (DragListener listener : mListeners) { 303 listener.onDragStart(source, dragInfo, dragAction); 304 } 305 306 int registrationX = ((int)mMotionDownX) - screenX; 307 int registrationY = ((int)mMotionDownY) - screenY; 308 309 final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left; 310 final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top; 311 mTouchOffsetX = mMotionDownX - screenX - dragRegionLeft; 312 mTouchOffsetY = mMotionDownY - screenY - dragRegionTop; 313 314 mDragging = true; 315 mDragSource = source; 316 mDragInfo = dragInfo; 317 318 mVibrator.vibrate(VIBRATE_DURATION); 319 320 DragView dragView = mDragView = new DragView(mContext, b, registrationX, registrationY, 321 textureLeft, textureTop, textureWidth, textureHeight); 322 323 if (dragRegion != null) { 324 dragView.setDragRegion(dragRegionLeft, dragRegion.top, 325 dragRegion.right - dragRegionLeft, dragRegion.bottom - dragRegionTop); 326 } 327 328 dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY); 329 330 handleMoveEvent((int) mMotionDownX, (int) mMotionDownY); 331 } 332 333 /** 334 * Draw the view into a bitmap. 335 */ 336 private Bitmap getViewBitmap(View v) { 337 v.clearFocus(); 338 v.setPressed(false); 339 340 boolean willNotCache = v.willNotCacheDrawing(); 341 v.setWillNotCacheDrawing(false); 342 343 // Reset the drawing cache background color to fully transparent 344 // for the duration of this operation 345 int color = v.getDrawingCacheBackgroundColor(); 346 v.setDrawingCacheBackgroundColor(0); 347 348 if (color != 0) { 349 v.destroyDrawingCache(); 350 } 351 v.buildDrawingCache(); 352 Bitmap cacheBitmap = v.getDrawingCache(); 353 if (cacheBitmap == null) { 354 Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException()); 355 return null; 356 } 357 358 Bitmap bitmap = Bitmap.createBitmap(cacheBitmap); 359 360 // Restore the view 361 v.destroyDrawingCache(); 362 v.setWillNotCacheDrawing(willNotCache); 363 v.setDrawingCacheBackgroundColor(color); 364 365 return bitmap; 366 } 367 368 /** 369 * Call this from a drag source view like this: 370 * 371 * <pre> 372 * @Override 373 * public boolean dispatchKeyEvent(KeyEvent event) { 374 * return mDragController.dispatchKeyEvent(this, event) 375 * || super.dispatchKeyEvent(event); 376 * </pre> 377 */ 378 @SuppressWarnings({"UnusedDeclaration"}) 379 public boolean dispatchKeyEvent(KeyEvent event) { 380 return mDragging; 381 } 382 383 /** 384 * Stop dragging without dropping. 385 */ 386 public void cancelDrag() { 387 endDrag(); 388 } 389 390 private void endDrag() { 391 if (mDragging) { 392 mDragging = false; 393 if (mOriginator != null) { 394 mOriginator.setVisibility(View.VISIBLE); 395 } 396 for (DragListener listener : mListeners) { 397 listener.onDragEnd(); 398 } 399 if (mDragView != null) { 400 mDragView.remove(); 401 mDragView = null; 402 } 403 } 404 } 405 406 /** 407 * Call this from a drag source view. 408 */ 409 public boolean onInterceptTouchEvent(MotionEvent ev) { 410 if (false) { 411 Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging=" 412 + mDragging); 413 } 414 final int action = ev.getAction(); 415 416 if (action == MotionEvent.ACTION_DOWN) { 417 recordScreenSize(); 418 } 419 420 final int screenX = clamp((int)ev.getRawX(), 0, mDisplayMetrics.widthPixels); 421 final int screenY = clamp((int)ev.getRawY(), 0, mDisplayMetrics.heightPixels); 422 423 switch (action) { 424 case MotionEvent.ACTION_MOVE: 425 break; 426 427 case MotionEvent.ACTION_DOWN: 428 // Remember location of down touch 429 mMotionDownX = screenX; 430 mMotionDownY = screenY; 431 mLastDropTarget = null; 432 break; 433 434 case MotionEvent.ACTION_CANCEL: 435 case MotionEvent.ACTION_UP: 436 if (mDragging) { 437 drop(screenX, screenY); 438 } 439 endDrag(); 440 break; 441 } 442 443 return mDragging; 444 } 445 446 /** 447 * Sets the view that should handle move events. 448 */ 449 void setMoveTarget(View view) { 450 mMoveTarget = view; 451 } 452 453 public boolean dispatchUnhandledMove(View focused, int direction) { 454 return mMoveTarget != null && mMoveTarget.dispatchUnhandledMove(focused, direction); 455 } 456 457 private void handleMoveEvent(int x, int y) { 458 mDragView.move(x, y); 459 460 // Drop on someone? 461 final int[] coordinates = mCoordinatesTemp; 462 DropTarget dropTarget = findDropTarget(x, y, coordinates); 463 if (dropTarget != null) { 464 DropTarget delegate = dropTarget.getDropTargetDelegate( 465 mDragSource, coordinates[0], coordinates[1], 466 (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo); 467 if (delegate != null) { 468 dropTarget = delegate; 469 } 470 471 if (mLastDropTarget != dropTarget) { 472 if (mLastDropTarget != null) { 473 mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1], 474 (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo); 475 } 476 dropTarget.onDragEnter(mDragSource, coordinates[0], coordinates[1], 477 (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo); 478 } 479 dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1], 480 (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo); 481 } else { 482 if (mLastDropTarget != null) { 483 mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1], 484 (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo); 485 } 486 } 487 mLastDropTarget = dropTarget; 488 489 // Scroll, maybe, but not if we're in the delete region. 490 boolean inDeleteRegion = false; 491 if (mDeleteRegion != null) { 492 inDeleteRegion = mDeleteRegion.contains(x, y); 493 } 494 495 // After a scroll, the touch point will still be in the scroll region. 496 // Rather than scrolling immediately, require a bit of twiddling to scroll again 497 final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop(); 498 mDistanceSinceScroll += 499 Math.sqrt(Math.pow(mLastTouch[0] - x, 2) + Math.pow(mLastTouch[1] - y, 2)); 500 mLastTouch[0] = x; 501 mLastTouch[1] = y; 502 503 if (!inDeleteRegion && x < mScrollZone) { 504 if (mScrollState == SCROLL_OUTSIDE_ZONE && mDistanceSinceScroll > slop) { 505 mScrollState = SCROLL_WAITING_IN_ZONE; 506 mScrollRunnable.setDirection(SCROLL_LEFT); 507 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY); 508 mDragScroller.onEnterScrollArea(SCROLL_LEFT); 509 } 510 } else if (!inDeleteRegion && x > mScrollView.getWidth() - mScrollZone) { 511 if (mScrollState == SCROLL_OUTSIDE_ZONE && mDistanceSinceScroll > slop) { 512 mScrollState = SCROLL_WAITING_IN_ZONE; 513 mScrollRunnable.setDirection(SCROLL_RIGHT); 514 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY); 515 mDragScroller.onEnterScrollArea(SCROLL_RIGHT); 516 } 517 } else { 518 if (mScrollState == SCROLL_WAITING_IN_ZONE) { 519 mScrollState = SCROLL_OUTSIDE_ZONE; 520 mScrollRunnable.setDirection(SCROLL_RIGHT); 521 mHandler.removeCallbacks(mScrollRunnable); 522 mDragScroller.onExitScrollArea(); 523 } 524 } 525 } 526 527 /** 528 * Call this from a drag source view. 529 */ 530 public boolean onTouchEvent(MotionEvent ev) { 531 if (!mDragging) { 532 return false; 533 } 534 535 final int action = ev.getAction(); 536 final int screenX = clamp((int)ev.getRawX(), 0, mDisplayMetrics.widthPixels); 537 final int screenY = clamp((int)ev.getRawY(), 0, mDisplayMetrics.heightPixels); 538 539 switch (action) { 540 case MotionEvent.ACTION_DOWN: 541 // Remember where the motion event started 542 mMotionDownX = screenX; 543 mMotionDownY = screenY; 544 545 if ((screenX < mScrollZone) || (screenX > mScrollView.getWidth() - mScrollZone)) { 546 mScrollState = SCROLL_WAITING_IN_ZONE; 547 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY); 548 } else { 549 mScrollState = SCROLL_OUTSIDE_ZONE; 550 } 551 break; 552 case MotionEvent.ACTION_MOVE: 553 handleMoveEvent(screenX, screenY); 554 break; 555 case MotionEvent.ACTION_UP: 556 mHandler.removeCallbacks(mScrollRunnable); 557 if (mDragging) { 558 drop(screenX, screenY); 559 } 560 endDrag(); 561 562 break; 563 case MotionEvent.ACTION_CANCEL: 564 cancelDrag(); 565 } 566 567 return true; 568 } 569 570 private boolean drop(float x, float y) { 571 final int[] coordinates = mCoordinatesTemp; 572 DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates); 573 574 if (dropTarget != null) { 575 dropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1], 576 (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo); 577 if (dropTarget.acceptDrop(mDragSource, coordinates[0], coordinates[1], 578 (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo)) { 579 dropTarget.onDrop(mDragSource, coordinates[0], coordinates[1], 580 (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo); 581 mDragSource.onDropCompleted((View) dropTarget, true); 582 return true; 583 } else { 584 mDragSource.onDropCompleted((View) dropTarget, false); 585 return true; 586 } 587 } else { 588 mDragSource.onDropCompleted(null, false); 589 } 590 return false; 591 } 592 593 private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) { 594 final Rect r = mRectTemp; 595 596 final ArrayList<DropTarget> dropTargets = mDropTargets; 597 final int count = dropTargets.size(); 598 for (int i=count-1; i>=0; i--) { 599 DropTarget target = dropTargets.get(i); 600 if (!target.isDropEnabled()) 601 continue; 602 603 target.getHitRect(r); 604 605 // Convert the hit rect to screen coordinates 606 target.getLocationOnScreen(dropCoordinates); 607 r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop()); 608 609 if (r.contains(x, y)) { 610 DropTarget delegate = target.getDropTargetDelegate(mDragSource, 611 x, y, (int)mTouchOffsetX, (int)mTouchOffsetY, mDragView, mDragInfo); 612 if (delegate != null) { 613 target = delegate; 614 target.getLocationOnScreen(dropCoordinates); 615 } 616 617 // Make dropCoordinates relative to the DropTarget 618 dropCoordinates[0] = x - dropCoordinates[0]; 619 dropCoordinates[1] = y - dropCoordinates[1]; 620 621 return target; 622 } 623 } 624 return null; 625 } 626 627 /** 628 * Get the screen size so we can clamp events to the screen size so even if 629 * you drag off the edge of the screen, we find something. 630 */ 631 private void recordScreenSize() { 632 ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) 633 .getDefaultDisplay().getMetrics(mDisplayMetrics); 634 } 635 636 /** 637 * Clamp val to be >= min and < max. 638 */ 639 private static int clamp(int val, int min, int max) { 640 if (val < min) { 641 return min; 642 } else if (val >= max) { 643 return max - 1; 644 } else { 645 return val; 646 } 647 } 648 649 public void setDragScoller(DragScroller scroller) { 650 mDragScroller = scroller; 651 } 652 653 public void setWindowToken(IBinder token) { 654 mWindowToken = token; 655 } 656 657 /** 658 * Sets the drag listner which will be notified when a drag starts or ends. 659 */ 660 public void addDragListener(DragListener l) { 661 mListeners.add(l); 662 } 663 664 /** 665 * Remove a previously installed drag listener. 666 */ 667 public void removeDragListener(DragListener l) { 668 mListeners.remove(l); 669 } 670 671 /** 672 * Add a DropTarget to the list of potential places to receive drop events. 673 */ 674 public void addDropTarget(DropTarget target) { 675 mDropTargets.add(target); 676 } 677 678 /** 679 * Don't send drop events to <em>target</em> any more. 680 */ 681 public void removeDropTarget(DropTarget target) { 682 mDropTargets.remove(target); 683 } 684 685 /** 686 * Set which view scrolls for touch events near the edge of the screen. 687 */ 688 public void setScrollView(View v) { 689 mScrollView = v; 690 } 691 692 /** 693 * Specifies the delete region. We won't scroll on touch events over the delete region. 694 * 695 * @param region The rectangle in screen coordinates of the delete region. 696 */ 697 void setDeleteRegion(RectF region) { 698 mDeleteRegion = region; 699 } 700 701 private class ScrollRunnable implements Runnable { 702 private int mDirection; 703 704 ScrollRunnable() { 705 } 706 707 public void run() { 708 if (mDragScroller != null) { 709 if (mDirection == SCROLL_LEFT) { 710 mDragScroller.scrollLeft(); 711 } else { 712 mDragScroller.scrollRight(); 713 } 714 mScrollState = SCROLL_OUTSIDE_ZONE; 715 mDistanceSinceScroll = 0; 716 mDragScroller.onExitScrollArea(); 717 } 718 } 719 720 void setDirection(int direction) { 721 mDirection = direction; 722 } 723 } 724} 725