1 /* 2 * Copyright (C) 2016 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 17 package android.support.v13.view; 18 19 20 import android.graphics.Point; 21 import android.support.v4.view.InputDeviceCompat; 22 import android.support.v4.view.MotionEventCompat; 23 import android.view.MotionEvent; 24 import android.view.View; 25 26 /** 27 * DragStartHelper is a utility class for implementing drag and drop support. 28 * <p> 29 * It detects gestures commonly used to start drag (long click for any input source, 30 * click and drag for mouse). 31 * <p> 32 * It also keeps track of the screen location where the drag started, and helps determining 33 * the hot spot position for a drag shadow. 34 * <p> 35 * Implement {@link DragStartHelper.OnDragStartListener} to start the drag operation: 36 * <pre> 37 * DragStartHelper.OnDragStartListener listener = new DragStartHelper.OnDragStartListener { 38 * protected void onDragStart(View view, DragStartHelper helper) { 39 * View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) { 40 * public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { 41 * super.onProvideShadowMetrics(shadowSize, shadowTouchPoint); 42 * helper.getTouchPosition(shadowTouchPoint); 43 * } 44 * }; 45 * view.startDrag(mClipData, shadowBuilder, mLocalState, mDragFlags); 46 * } 47 * }; 48 * mDragStartHelper = new DragStartHelper(mDraggableView, listener); 49 * </pre> 50 * Once created, DragStartHelper can be attached to a view (this will replace existing long click 51 * and touch listeners): 52 * <pre> 53 * mDragStartHelper.attach(); 54 * </pre> 55 * It may also be used in combination with existing listeners: 56 * <pre> 57 * public boolean onTouch(View view, MotionEvent event) { 58 * if (mDragStartHelper.onTouch(view, event)) { 59 * return true; 60 * } 61 * return handleTouchEvent(view, event); 62 * } 63 * public boolean onLongClick(View view) { 64 * if (mDragStartHelper.onLongClick(view)) { 65 * return true; 66 * } 67 * return handleLongClickEvent(view); 68 * } 69 * </pre> 70 */ 71 public class DragStartHelper { 72 final private View mView; 73 final private OnDragStartListener mListener; 74 75 private int mLastTouchX, mLastTouchY; 76 77 /** 78 * Interface definition for a callback to be invoked when a drag start gesture is detected. 79 */ 80 public interface OnDragStartListener { 81 /** 82 * Called when a drag start gesture has been detected. 83 * 84 * @param v The view over which the drag start gesture has been detected. 85 * @param helper The DragStartHelper object which detected the gesture. 86 * @return True if the listener has consumed the event, false otherwise. 87 */ onDragStart(View v, DragStartHelper helper)88 boolean onDragStart(View v, DragStartHelper helper); 89 } 90 91 /** 92 * Create a DragStartHelper associated with the specified view. 93 * The newly created helper is not initially attached to the view, {@link #attach} must be 94 * called explicitly. 95 * @param view A View 96 */ DragStartHelper(View view, OnDragStartListener listener)97 public DragStartHelper(View view, OnDragStartListener listener) { 98 mView = view; 99 mListener = listener; 100 } 101 102 /** 103 * Attach the helper to the view. 104 * <p> 105 * This will replace previously existing touch and long click listeners. 106 */ attach()107 public void attach() { 108 mView.setOnLongClickListener(mLongClickListener); 109 mView.setOnTouchListener(mTouchListener); 110 } 111 112 /** 113 * Detach the helper from the view. 114 * <p> 115 * This will reset touch and long click listeners to {@code null}. 116 */ detach()117 public void detach() { 118 mView.setOnLongClickListener(null); 119 mView.setOnTouchListener(null); 120 } 121 122 /** 123 * Handle a touch event. 124 * @param v The view the touch event has been dispatched to. 125 * @param event The MotionEvent object containing full information about 126 * the event. 127 * @return True if the listener has consumed the event, false otherwise. 128 */ onTouch(View v, MotionEvent event)129 public boolean onTouch(View v, MotionEvent event) { 130 if (event.getAction() == MotionEvent.ACTION_DOWN || 131 event.getAction() == MotionEvent.ACTION_MOVE) { 132 mLastTouchX = (int) event.getX(); 133 mLastTouchY = (int) event.getY(); 134 } 135 if (event.getAction() == MotionEvent.ACTION_MOVE && 136 MotionEventCompat.isFromSource(event, InputDeviceCompat.SOURCE_MOUSE) && 137 (MotionEventCompat.getButtonState(event) & MotionEventCompat.BUTTON_PRIMARY) != 0) { 138 return mListener.onDragStart(v, this); 139 } 140 return false; 141 } 142 143 /** 144 * Handle a long click event. 145 * @param v The view that was clicked and held. 146 * @return true if the callback consumed the long click, false otherwise. 147 */ onLongClick(View v)148 public boolean onLongClick(View v) { 149 return mListener.onDragStart(v, this); 150 } 151 152 /** 153 * Compute the position of the touch event that started the drag operation. 154 * @param point The position of the touch event that started the drag operation. 155 */ getTouchPosition(Point point)156 public void getTouchPosition(Point point) { 157 point.set(mLastTouchX, mLastTouchY); 158 } 159 160 private final View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() { 161 @Override 162 public boolean onLongClick(View v) { 163 return DragStartHelper.this.onLongClick(v); 164 } 165 }; 166 167 private final View.OnTouchListener mTouchListener = new View.OnTouchListener() { 168 @Override 169 public boolean onTouch(View v, MotionEvent event) { 170 return DragStartHelper.this.onTouch(v, event); 171 } 172 }; 173 } 174 175