我们从主Activity即Launcher.java的布局文件即launcher.xml看起
<!--LauncherRootView最终继承自FrameLayout-->
<com.android.launcher3.LauncherRootView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/launcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!--DragLayer最终继承自FrameLayout-->
<com.android.launcher3.DragLayer
android:id="@+id/drag_layer"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--显示焦点的半透明背景-->
<com.android.launcher3.FocusIndicatorView
android:id="@+id/focus_indicator"
android:layout_width="52dp"
android:layout_height="52dp" />
<!-- The workspace contains 5 screens of cells -->
<!--装载应用图标的自定义视图,直接继承于PagedView,而
PagedView继承自ViewGroup,包括所有屏,Launcher2时默认有
五屏-->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
launcher:defaultScreen="@integer/config_workspaceDefaultScreen"
launcher:pageIndicator="@+id/page_indicator">
</com.android.launcher3.Workspace>
<!-- DO NOT CHANGE THE ID -->
<!--Workspace下方的四个固定图标(不随屏幕滑动)和一个抽屉菜单-->
<include layout="@layout/hotseat"
android:id="@+id/hotseat"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!--长按桌面空白处显示的菜单-->
<include layout="@layout/overview_panel"
android:id="@+id/overview_panel"
android:visibility="gone" />
<!-- Keep these behind the workspace so that they are not visible when
we go into AllApps -->
<!--Workspace和Hotseat中间的几个原点,反馈滑动到了第几屏-->
<include
android:id="@+id/page_indicator"
layout="@layout/page_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
<!--默认桌面上有google搜索框,但也是删除卸载应用信息的占位视图-->
<include
android:id="@+id/search_drop_target_bar"
layout="@layout/search_drop_target_bar" />
<!--装载小部件的视图,WidgetsContainerView直接继承自BaseContainerView
最终继承自FrameLayout-->
<include layout="@layout/widgets_view"
android:id="@+id/widgets_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
<!--显示所有应用的视图,AllAppsContainerView直接继承自BaseContainerView
最终继承自FrameLayout-->
<include layout="@layout/all_apps"
android:id="@+id/apps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
</com.android.launcher3.DragLayer>
</com.android.launcher3.LauncherRootView>
由于LauncherRootView并没有重写事件处理的相关方法,我们从DragLayer开始,以从workspace上面拖动一个图标到新的位置为例,我们查看它关于事件分发与处理的函数重写
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN) {
if (handleTouchDown(ev, true)) {
return true;
}
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
if (mTouchCompleteListener != null) {
mTouchCompleteListener.onTouchComplete();
}
mTouchCompleteListener = null;
}
clearAllResizeFrames();
return mDragController.onInterceptTouchEvent(ev);
}
private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
Rect hitRect = new Rect();
int x = (int) ev.getX();
int y = (int) ev.getY();
if (mBlockTouches) {
return true;
}
for (AppWidgetResizeFrame child: mResizeFrames) {
child.getHitRect(hitRect);
if (hitRect.contains(x, y)) {
if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) {
mCurrentResizeFrame = child;
mXDown = x;
mYDown = y;
requestDisallowInterceptTouchEvent(true);
return true;
}
}
}
Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
if (currentFolder != null && intercept) {
if (currentFolder.isEditingName()) {
if (!isEventOverFolderTextRegion(currentFolder, ev)) {
currentFolder.dismissEditingName();
return true;
}
}
if (!isEventOverFolder(currentFolder, ev)) {
if (isInAccessibleDrag()) {
// Do not close the folder if in drag and drop.
if (!isEventOverDropTargetBar(ev)) {
return true;
}
} else {
mLauncher.closeFolder();
return true;
}
}
}
return false;
}
普通的应用图标,这里不考虑小部件和文件夹的问题,handleTouchDown(ev, true)方法返回false最后执行了DragController的onInterceptTouchEvent(MotionEvent ev)方法,同时它没有对Move做任何处理,直接交给DragController
public boolean onInterceptTouchEvent(MotionEvent ev) {
@SuppressWarnings("all") // suppress dead code warning
final boolean debug = false;
if (mIsAccessibleDrag) {
return false;
}
// Update the velocity tracker
acquireVelocityTrackerAndAddMovement(ev);
final int action = ev.getAction();
final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
final int dragLayerX = dragLayerPos[0];
final int dragLayerY = dragLayerPos[1];
switch (action) {
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_DOWN:
// Remember location of down touch
mMotionDownX = dragLayerX;
mMotionDownY = dragLayerY;
mLastDropTarget = null;
break;
case MotionEvent.ACTION_UP:
mLastTouchUpTime = System.currentTimeMillis();
if (mDragging) {
PointF vec = isFlingingToDelete(mDragObject.dragSource);
if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) {
vec = null;
}
if (vec != null) {
dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
} else {
drop(dragLayerX, dragLayerY);
}
}
endDrag();
break;
case MotionEvent.ACTION_CANCEL:
cancelDrag();
break;
}
return mDragging;
}
将不同的拖拽源事件统一传入DragController进行处理,这里返回false,未拦截,进入Workspace,Move过程未处理同样交给Workspace
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "Workspace.ACTION_DOWN");
mXDown = ev.getX();
mYDown = ev.getY();
mTouchDownTime = System.currentTimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "Workspace.ACTION_UP");
if (mTouchState == TOUCH_STATE_REST) {
final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
if (currentPage != null) {
onWallpaperTap(ev);
}
}
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
return super.onInterceptTouchEvent(ev);
}
Workspace可看作一个自定义的ViewGroup,因此可重写它的事件拦截方法,这里它执行了父类(PagedView)的方法super.onInterceptTouchEvent(ev),Move过程未处理,交给父类
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
acquireVelocityTrackerAndAddMovement(ev);
if (getChildCount() <= 0) {
LauncherLog.d(TAG, "There are no pages to swipe, page count = " + getChildCount());
return super.onInterceptTouchEvent(ev);
}
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) &&
(mTouchState == TOUCH_STATE_SCROLLING)) {
LauncherLog.d(TAG, "onInterceptTouchEvent: touch move during scrolling.");
return true;
}
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
if (mActivePointerId != INVALID_POINTER) {
determineScrollingStart(ev);
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) { return true; }
final float x = ev.getX(pointerIndex);
final float deltaX = mLastX + mLastMotionXRemainder - x;
mLastX = x;
if (mTouchState == TOUCH_STATE_SCROLLING) {
mTotalMotionX += Math.abs(deltaX);
if (Math.abs(deltaX) >= 1.0f) {
mTouchX += deltaX;
mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
scrollBy((int) deltaX, 0);
mLastMotionX = x;
mLastMotionXRemainder = deltaX - (int) deltaX;
} else {
awakenScrollBars();
}
}
}
break;
}
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
// Remember location of down touch
mDownMotionX = x;
mDownMotionY = y;
mDownScrollX = getScrollX();
mLastMotionX = x;
/// M: [Performance] Scroll page in first performTraversal to improve response time.
mLastX = mLastMotionX;
mLastMotionY = y;
float[] p = mapPointFromViewToParent(this, x, y);
mParentDownMotionX = p[0];
mParentDownMotionY = p[1];
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3);
if (finishedScrolling) {
mTouchState = TOUCH_STATE_REST;
if (!mScroller.isFinished() && !mFreeScroll) {
setCurrentPage(getNextPage());
pageEndMoving();
}
} else {
if (isTouchPointInViewportWithBuffer((int) mDownMotionX, (int) mDownMotionY)) {
mTouchState = TOUCH_STATE_SCROLLING;
} else {
mTouchState = TOUCH_STATE_REST;
}
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
resetTouchState();
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
releaseVelocityTracker();
break;
}
return mTouchState != TOUCH_STATE_REST;
}
未拦截,交给它的子view即CellLayout的方法onInterceptTouchEvent(MotionEvent ev)
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if (mUseTouchHelper ||
(mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev))) {
return true;
}
return false;
}
未拦截,继续交给它的子view即BubbleTextView,它继承自TextView,用来显示应用的图标与名称,
到这里就已经是最小的子View了,执行它的onTouchEvent(MotionEvent event)方法
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
if (mStylusEventHelper.checkAndPerformStylusEvent(event)) {
mLongPressHelper.cancelLongPress();
result = true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mDeferShadowGenerationOnTouch && mPressedBackground == null) {
mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
}
if (!mStylusEventHelper.inStylusButtonPressed()) {
mLongPressHelper.postCheckForLongPress();
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (!isPressed()) {
mPressedBackground = null;
}
mLongPressHelper.cancelLongPress();
break;
case MotionEvent.ACTION_MOVE:
if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
mLongPressHelper.cancelLongPress();
}
break;
}
return result;
}
Down事件处理完成后层层交付直到Activity即Launcher,如果是长按会响应onLongClick(View v)
public boolean onLongClick(View v) {
if (!isDraggingEnabled()) return false;
if (isWorkspaceLocked()) return false;
if (mState != State.WORKSPACE) return false;
if (v == mAllAppsButton) {
onLongClickAllAppsButton(v);
return true;
}
if (v instanceof Workspace) {
if (!mWorkspace.isInOverviewMode()) {
if (!mWorkspace.isTouchActive()) {
showOverviewMode(true);
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
return true;
} else {
return false;
}
} else {
return false;
}
}
CellLayout.CellInfo longClickCellInfo = null;
View itemUnderLongClick = null;
if (v.getTag() instanceof ItemInfo) {
ItemInfo info = (ItemInfo) v.getTag();
longClickCellInfo = new CellLayout.CellInfo(v, info);
itemUnderLongClick = longClickCellInfo.cell;
resetAddInfo();
}
// The hotseat touch handling does not go through Workspace, and we always allow long press
// on hotseat items.
final boolean inHotseat = isHotseatLayout(v);
if (!mDragController.isDragging()) {
if (itemUnderLongClick == null) {
// User long pressed on empty space
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
if (mWorkspace.isInOverviewMode()) {
mWorkspace.startReordering(v);
} else {
showOverviewMode(true);
}
} else {
final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank(
mHotseat.getOrderInHotseat(
longClickCellInfo.cellX,
longClickCellInfo.cellY));
if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
// User long pressed on an item
mWorkspace.startDrag(longClickCellInfo);
}
}
}
return true;
}
执行mWorkspace.startDrag(longClickCellInfo)
public void startDrag(CellLayout.CellInfo cellInfo) {
startDrag(cellInfo, false);
}
@Override
public void startDrag(CellLayout.CellInfo cellInfo, boolean accessible) {
View child = cellInfo.cell;
if (child != null && child.getTag() == null) {
LauncherLog.d(TAG, "Abnormal start drag: cellInfo = " + cellInfo + ",child = " + child);
return;
}
// Make sure the drag was started by a long press as opposed to a long click.
if (!child.isInTouchMode()) {
if (LauncherLog.DEBUG) {
LauncherLog.i(TAG, "The child " + child + " is not in touch mode.");
}
return;
}
///M : ALPS02375941
boolean isValid = (child.getParent().getParent()) instanceof CellLayout;
if (!isValid) {
return ;
}
mDragInfo = cellInfo;
child.setVisibility(INVISIBLE);
CellLayout layout = (CellLayout) child.getParent().getParent();
layout.prepareChildForDrag(child);
beginDragShared(child, this, accessible);
}
public void beginDragShared(View child, DragSource source, boolean accessible) {
beginDragShared(child, new Point(), source, accessible);
}
public void beginDragShared(View child, Point relativeTouchPos, DragSource source,
boolean accessible) {
//清除拖动对象的焦点
child.clearFocus();
child.setPressed(false);
//创建这个将要被拖动的图标的轮廓
mDragOutline = createDragOutline(child, DRAG_BITMAP_PADDING);
mLauncher.onDragStarted(child);
// 让这个图标的轮廓跟随图标的拖动而移动
AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
//创建一个拖动的对象
final Bitmap b = createDragBitmap(child, padding);
final int bmpWidth = b.getWidth();
final int bmpHeight = b.getHeight();
float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
- padding.get() / 2);
DeviceProfile grid = mLauncher.getDeviceProfile();
Point dragVisualizeOffset = null;
Rect dragRect = null;
if (child instanceof BubbleTextView) {
BubbleTextView icon = (BubbleTextView) child;
int iconSize = grid.iconSizePx;
int top = child.getPaddingTop();
int left = (bmpWidth - iconSize) / 2;
int right = left + iconSize;
int bottom = top + iconSize;
if (icon.isLayoutHorizontal()) {
if (icon.getIcon().getBounds().contains(relativeTouchPos.x, relativeTouchPos.y)) {
dragLayerX = Math.round(mTempXY[0]);
} else {
dragLayerX = Math.round(mTempXY[0] + relativeTouchPos.x - (bmpWidth / 2));
}
}
dragLayerY += top;
dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
dragRect = new Rect(left, top, right, bottom);
} else if (child instanceof FolderIcon) {
int previewSize = grid.folderIconSizePx;
dragVisualizeOffset = new Point(-padding.get() / 2,
padding.get() / 2 - child.getPaddingTop());
dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
}
if (child instanceof BubbleTextView) {
BubbleTextView icon = (BubbleTextView) child;
icon.clearPressedBackground();
}
if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
String msg = "Drag started with a view that has no tag set. This "
+ "will cause a crash (issue 11627249) down the line. "
+ "View: " + child + " tag: " + child.getTag();
throw new IllegalStateException(msg);
}
if (child.getParent() instanceof ShortcutAndWidgetContainer) {
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
}
//创建拖动的视图
DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible);
dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
b.recycle();
}
最后执行了DragController.startDrag()
public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo,
Rect viewImageBounds, int dragAction, float initialDragViewScale) {
int[] loc = mCoordinatesTemp;
mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
int dragLayerX = loc[0] + viewImageBounds.left
+ (int) ((initialDragViewScale * bmp.getWidth() - bmp.getWidth()) / 2);
int dragLayerY = loc[1] + viewImageBounds.top
+ (int) ((initialDragViewScale * bmp.getHeight() - bmp.getHeight()) / 2);
startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, dragAction, null,
null, initialDragViewScale, false);
if (dragAction == DRAG_ACTION_MOVE) {
v.setVisibility(View.GONE);
}
}
public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY,
DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion,
float initialDragViewScale, boolean accessible) {
if (PROFILE_DRAWING_DURING_DRAG) {
android.os.Debug.startMethodTracing("Launcher");
}
// Hide soft keyboard, if visible
if (mInputMethodManager == null) {
mInputMethodManager = (InputMethodManager)
mLauncher.getSystemService(Context.INPUT_METHOD_SERVICE);
}
mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
for (DragListener listener : mListeners) {
listener.onDragStart(source, dragInfo, dragAction);
}
final int registrationX = mMotionDownX - dragLayerX;
final int registrationY = mMotionDownY - dragLayerY;
final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
mDragging = true;
mIsAccessibleDrag = accessible;
mDragObject = new DropTarget.DragObject();
mDragObject.dragComplete = false;
if (mIsAccessibleDrag) {
// For an accessible drag, we assume the view is being dragged from the center.
mDragObject.xOffset = b.getWidth() / 2;
mDragObject.yOffset = b.getHeight() / 2;
mDragObject.accessibleDrag = true;
} else {
mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
}
mDragObject.dragSource = source;
mDragObject.dragInfo = dragInfo;
final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
registrationY, 0, 0, b.getWidth(), b.getHeight(), initialDragViewScale);
if (dragOffset != null) {
dragView.setDragVisualizeOffset(new Point(dragOffset));
}
if (dragRegion != null) {
dragView.setDragRegion(new Rect(dragRegion));
}
mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
//将我们前面创建的拖动视图显示在DrayLayer上,这个show方法将启用一个拾取动画
dragView.show(mMotionDownX, mMotionDownY);
handleMoveEvent(mMotionDownX, mMotionDownY);
return dragView;
}
private void handleMoveEvent(int x, int y) {
mDragObject.dragView.move(x, y);
// Drop on someone?
final int[] coordinates = mCoordinatesTemp;
//查找当前位置对应的拖拽目标
DropTarget dropTarget = findDropTarget(x, y, coordinates);
mDragObject.x = coordinates[0];
mDragObject.y = coordinates[1];
//检查拖拽时的状态
checkTouchMove(dropTarget);
// Check if we are hovering over the scroll areas
//检查是否在滚动区域上方
mDistanceSinceScroll += Math.hypot(mLastTouch[0] - x, mLastTouch[1] - y);
mLastTouch[0] = x;
mLastTouch[1] = y;
checkScrollState(x, y);
}
//遍历找出拖拽目的
private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
final Rect r = mRectTemp;
final ArrayList<DropTarget> dropTargets = mDropTargets;
final int count = dropTargets.size();
for (int i=count-1; i>=0; i--) {
DropTarget target = dropTargets.get(i);
if (!target.isDropEnabled())
continue;
target.getHitRectRelativeToDragLayer(r);
mDragObject.x = x;
mDragObject.y = y;
if (r.contains(x, y)) {
dropCoordinates[0] = x;
dropCoordinates[1] = y;
mLauncher.getDragLayer().mapCoordInSelfToDescendent((View) target, dropCoordinates);
return target;
}
}
return null;
}
private void checkTouchMove(DropTarget dropTarget) {
if (dropTarget != null) {
if (mLastDropTarget != dropTarget) {
if (mLastDropTarget != null) {
mLastDropTarget.onDragExit(mDragObject);
}
dropTarget.onDragEnter(mDragObject);
}
dropTarget.onDragOver(mDragObject);
} else {
if (mLastDropTarget != null) {
mLastDropTarget.onDragExit(mDragObject);
}
}
mLastDropTarget = dropTarget;
}
移动前后dropTarget一致,故执行onDragOver(mDragObject),我们是在Workspace上进行的操作,而Workspace也的确实现了DropTarget接口,所以我们接下来进入Workspace的onDragOver()方法。执行完onDragOver()后,Launcher的LongClick就彻底执行完毕了,接下来事件交给DragLayer的onTouchEvent()方法进行处理
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
int action = ev.getAction();
int x = (int) ev.getX();
int y = (int) ev.getY();
if (action == MotionEvent.ACTION_UP) {
LauncherLog.d(TAG, "[PerfTest --> drag widget] start process");
}
if (mBlockTouches) {
return true;
}
if (action == MotionEvent.ACTION_DOWN) {
if (handleTouchDown(ev, false)) {
return true;
}
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
if (mTouchCompleteListener != null) {
mTouchCompleteListener.onTouchComplete();
}
mTouchCompleteListener = null;
}
if (mCurrentResizeFrame != null) {
handled = true;
switch (action) {
case MotionEvent.ACTION_MOVE:
mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
mCurrentResizeFrame.onTouchUp();
mCurrentResizeFrame = null;
}
}
if (handled) return true;
return mDragController.onTouchEvent(ev);
}
进入DragController的onTouchEvent()方法
public boolean onTouchEvent(MotionEvent ev) {
if (!mDragging || mIsAccessibleDrag) {
return false;
}
// Update the velocity tracker
acquireVelocityTrackerAndAddMovement(ev);
final int action = ev.getAction();
final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
final int dragLayerX = dragLayerPos[0];
final int dragLayerY = dragLayerPos[1];
switch (action) {
case MotionEvent.ACTION_DOWN:
// Remember where the motion event started
mMotionDownX = dragLayerX;
mMotionDownY = dragLayerY;
if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
mScrollState = SCROLL_WAITING_IN_ZONE;
mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
} else {
mScrollState = SCROLL_OUTSIDE_ZONE;
}
handleMoveEvent(dragLayerX, dragLayerY);
break;
case MotionEvent.ACTION_MOVE:
handleMoveEvent(dragLayerX, dragLayerY);
break;
case MotionEvent.ACTION_UP:
// Ensure that we've processed a move event at the current pointer location.
handleMoveEvent(dragLayerX, dragLayerY);
mHandler.removeCallbacks(mScrollRunnable);
if (mDragging) {
PointF vec = isFlingingToDelete(mDragObject.dragSource);
if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) {
vec = null;
}
if (vec != null) {
dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
} else {
drop(dragLayerX, dragLayerY);
}
}
endDrag();
break;
case MotionEvent.ACTION_CANCEL:
mHandler.removeCallbacks(mScrollRunnable);
cancelDrag();
break;
}
return true;
}
private void drop(float x, float y) {
final int[] coordinates = mCoordinatesTemp;
final DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
mDragObject.x = coordinates[0];
mDragObject.y = coordinates[1];
boolean accepted = false;
if (dropTarget != null) {
mDragObject.dragComplete = true;
//通知拖拽目的对象已离开
dropTarget.onDragExit(mDragObject);
if (dropTarget.acceptDrop(mDragObject)) {//如果支持放入该拖拽目的
dropTarget.onDrop(mDragObject); //将拖拽对象放入拖拽目的
accepted = true;
}
}
mDragObject.dragSource.onDropCompleted((View) dropTarget, mDragObject, false, accepted);
}
这里又执行了handleMoveEvent()方法,同上进入Workspace的onDragOver(),整个拖动的
过程不断重复上述同程,如果从一个位置(DropTarget)移出则执行Workspace的onDragExit()方法如果成功移到一个新的位置,最后的UP事件执行Workspace的acceptDrop(DragObject d)、onDrop(DragObject d)、onDropCompleted(final View target, final DragObject d, final boolean isFlingToDelete, final boolean success)然后执行了DragController的endDrag()
private void endDrag() {
if (mDragging) {
mDragging = false;
mIsAccessibleDrag = false;
clearScrollRunnable();
boolean isDeferred = false;
if (mDragObject.dragView != null) {
isDeferred = mDragObject.deferDragViewCleanupPostAnimation;
if (!isDeferred) {
mDragObject.dragView.remove();
}
mDragObject.dragView = null;
}
// Only end the drag if we are not deferred
if (!isDeferred) {
for (DragListener listener : new ArrayList<>(mListeners)) {
listener.onDragEnd();
}
}
}
releaseVelocityTracker();
}
Workspace实现了DragController.DragListener接口,所以最后是Workspace的onDragEnd()方法
最后补充说明一下DropTarget这个接口
/**
* Interface defining an object that can receive a drag.
* 定义一个可以接收拖动对象的接口,比如Workspace、Folder、ButtonDropTarget,它们都实现了此接口
*/
public interface DropTarget {
void onDrop( DragObject dragObject ); //当dragview被放下时调用
void onDragEnter( DragObject dragObject ); //进入一个dropTarget时调用
void onDragOver( DragObject dragObject ); //dropTarget不变时调用
void onDragExit( DragObject dragObject ); //退出一个droptarget时调用
void onFlingToDeletee(DragObject dragObject, PointF vec); //删除dragview
boolean acceptDrop( DragObject dragObject ); //判断是否可以放入
}