Android学习历程--Launcher拖拽流程
来源:互联网 发布:oauth2.0 java 服务端 编辑:程序博客网 时间:2024/06/06 16:36
Launcher之图标拖拽事件学习——
仔细想想我们每次对手机上的一个图标(app)进行卸载、拖动换地儿、整理图标、归档到文件夹等是否感觉是很简单的操作呢?实则不然,其将通过一些列的判断来进行效果的达到,今天通过代码的学习来一步一步的查看,到底每一次都发生了什么以及如何处理
每一次对于图标的操作都是长按对不对?不信的话拿过你的手机尝试一下?没错吧? 那么我们就从长按的响应事件为入口进行代码剖析。
一、长按响应
<pre name="code" class="java"><pre name="code" class="java">public boolean onLongClick(View v)
<span style="white-space:pre"></span>public boolean onLongClick(View v) { <span style="white-space:pre"></span>if (LauncherLog.DEBUG) {<span style="white-space:pre"></span>LauncherLog.d(TAG, "onLongClick: View = " + v + ", v.getTag() = " + v.getTag() + ", mState = " + mState);<span style="white-space:pre"></span>}<span style="white-space:pre"></span>if (!isDraggingEnabled()) {//允许拖拽与否<span style="white-space:pre"></span>LauncherLog.d(TAG, "onLongClick: isDraggingEnabled() = " + isDraggingEnabled());<span style="white-space:pre"></span>return false;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>if (isWorkspaceLocked()) {//WorkSpace是否锁定<span style="white-space:pre"></span>LauncherLog.d(TAG, "onLongClick: isWorkspaceLocked() mWorkspaceLoading " + mWorkspaceLoading<span style="white-space:pre"></span>+ ", mWaitingForResult = " + mWaitingForResult);<span style="white-space:pre"></span>return false;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>if (mState != State.WORKSPACE){//是否处于WorkSpace状态<span style="white-space:pre"></span>LauncherLog.d(TAG, "onLongClick: mState != State.WORKSPACE: = " + mState);<span style="white-space:pre"></span>return false;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>if (v instanceof Workspace) {<span style="white-space:pre"></span>LauncherLog.d(TAG, "v instanceof Workspace");<span style="white-space:pre"></span>if (!mWorkspace.isInOverviewMode()) {// 判断是否在缩略图模式下<span style="white-space:pre"></span>if (mWorkspace.enterOverviewMode()) {// 进入缩略图模式 <span style="white-space:pre"></span>mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,<span style="white-space:pre"></span>HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);<span style="white-space:pre"></span>return true;<span style="white-space:pre"></span>} else {<span style="white-space:pre"></span>return false;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>} else {<span style="white-space:pre"></span>return false;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>}<span style="white-space:pre"></span>CellLayout.CellInfo longClickCellInfo = null;<span style="white-space:pre"></span>View itemUnderLongClick = null;<span style="white-space:pre"></span>if (v.getTag() instanceof ItemInfo) {// ItemInfo子类<span style="white-space:pre"></span>ItemInfo info = (ItemInfo) v.getTag();<span style="white-space:pre"></span>longClickCellInfo = new CellLayout.CellInfo(v, info);<span style="white-space:pre"></span>;<span style="white-space:pre"></span>itemUnderLongClick = longClickCellInfo.cell;<span style="white-space:pre"></span>resetAddInfo();<span style="white-space:pre"></span>}<span style="white-space:pre"></span>// The hotseat touch handling does not go through Workspace, and we<span style="white-space:pre"></span>// always allow long press<span style="white-space:pre"></span>// on hotseat items.<span style="white-space:pre"></span>final boolean inHotseat = isHotseatLayout(v);// 是否热键栏<span style="white-space:pre"></span>boolean allowLongPress = inHotseat || mWorkspace.allowLongPress();//是否允许长按处理 <span style="white-space:pre"></span>if (allowLongPress && !mDragController.isDragging()) {// 允许长按 && 没有进行拖拽 <span style="white-space:pre"></span>if (itemUnderLongClick == null) {// 如果itemUnderLongClick为null,进行长按空白处一样的处理 <span style="white-space:pre"></span>// User long pressed on empty space<span style="white-space:pre"></span>mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,<span style="white-space:pre"></span>HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);<span style="white-space:pre"></span>if (mWorkspace.isInOverviewMode()) {<span style="white-space:pre"></span>mWorkspace.startReordering(v);<span style="white-space:pre"></span>} else {<span style="white-space:pre"></span>mWorkspace.enterOverviewMode();<span style="white-space:pre"></span>}<span style="white-space:pre"></span>} else {<span style="white-space:pre"></span>final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank(<span style="white-space:pre"></span>mHotseat.getOrderInHotseat(longClickCellInfo.cellX, longClickCellInfo.cellY));<span style="white-space:pre"></span> // 判断长按是否allapp按钮<span style="white-space:pre"></span>if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {// 长按的不是allapp按钮也不在文件夹展开的布局中 <span style="white-space:pre"></span>/// M: Call the appropriate callback for the IMtkWidget on<span style="white-space:pre"></span>/// the current page<span style="white-space:pre"></span>/// when long click and begin to drag IMtkWidget.<span style="white-space:pre"></span>mWorkspace.startDragAppWidget(mWorkspace.getCurrentPage());<span style="white-space:pre"></span>// User long pressed on an item<span style="white-space:pre"></span>mWorkspace.startDrag(longClickCellInfo);// 开始拖拽 <span style="white-space:pre"></span>}<span style="white-space:pre"></span>}<span style="white-space:pre"></span>}<span style="white-space:pre"></span>return true;<span style="white-space:pre"></span>}以上为对于长按事件的判断最后可以从代码中得出结论待到一切条件成立:
1、允许拖拽isDraggingEnabled()
2、WorkSpace是否被锁定isWorkspaceLocked()
3、是否处于WorkSpace状态mState != State.WORKSPACE
4、当前长按的地方是否有Item如果没有判断是否处于缩略图模式如果在不处理假若不在进入缩略图模式
5、获取到CellInfo
6、判断是否为Hotseat,进而判断是否允许长按处理然后如果itemUnderLongClick为null,进行长按空白处一样的处理 ,即进入缩略图模式。
6、判断是否为Hotseat,进而判断是否允许长按处理然后如果itemUnderLongClick为null,进行长按空白处一样的处理 ,即进入缩略图模式。
7、判断长按的不是allapp按钮也不在文件夹展开的布局中
8、拖拽widget或者item
二、拖拽
void startDrag(CellLayout.CellInfo cellInfo)
void startDrag(CellLayout.CellInfo cellInfo) { View child = cellInfo.cell; if (LauncherLog.DEBUG_DRAG) { LauncherLog.d(TAG, "startDrag cellInfo = " + cellInfo + ",child = " + child); } /// M: [ALPS01263567] Abnormal case, if user long press on all apps button and then /// long press on other shortcuts in hotseat, the dragInfo will be /// null, exception will happen, so need return directly. 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; } mDragInfo = cellInfo;// 更新单元信息 child.setVisibility(INVISIBLE);// 拖拽对象在原来的位置设为不可见可以让其他item进行占用该位置 CellLayout layout = (CellLayout) child.getParent().getParent();// 拖拽对象所在的屏幕 layout.prepareChildForDrag(child); beginDragShared(child, this); } public void beginDragShared(View child, DragSource source) { child.clearFocus(); child.setPressed(false); // The outline is used to visualize where the item will land if dropped // 创建拖拽对象投射轮廓 mDragOutline = createDragOutline(child, DRAG_BITMAP_PADDING); mLauncher.onDragStarted(child); // The drag bitmap follows the touch point around on the screen 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); if (LauncherLog.DEBUG_DRAG) { LauncherLog.d(TAG, "beginDragShared: child = " + child + ", source = " + source + ", dragLayerX = " + dragLayerX + ", dragLayerY = " + dragLayerY); } LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); Point dragVisualizeOffset = null; Rect dragRect = null; if (child instanceof BubbleTextView) { int iconSize = grid.iconSizePx; int top = child.getPaddingTop(); int left = (bmpWidth - iconSize) / 2; int right = left + iconSize; int bottom = top + iconSize; dragLayerY += top; // Note: The drag region is used to calculate drag layer offsets, but the // dragVisualizeOffset in addition to the dragRect (the size) to position the outline. 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; dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize); } // Clear the pressed state if necessary 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); } // 创建拖拽视图 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale); dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); if (child.getParent() instanceof ShortcutAndWidgetContainer) { mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent(); } b.recycle(); }1、开始拖拽
2、判断是否在TouchMode模式下并判断是否
3、更新单元信息,设置拖拽对象不可见然使之可以进行被其他item占用
4、beginDragShared
5、按照一定成的对所拖拽的对象进行投影绘制
6、OnDragStarted->创建拖拽对象->创建拖拽视图(重难点)
7、BitMap回收(注 Bitmap:
位图是位的数组,它制订了像素矩阵中各像素的颜色。
亦称为点阵图像或绘制图像,是由称作像素(图片元素)的单个点组成的。这些点可以进行不同的排列和染色以构成图样。当放大位图时,可以看见赖以构成整个图像的无数单个方块。扩大位图尺寸的效果是增多单个像素,从而使线条和形状显得参差不齐。然而,如果从稍远的位置观看它,位图图像的颜色和形状又显得是连续的。在体检时,工作人员会给你一个本子,在这个本子上有一些图像,而图像都是由一个个的点组成的,这和位图图像其实是差不多的。由于每一个像素都是单独染色的,您可以通过以每次一个像素的频率操作选择区域而产生近似相片的逼真效果,诸如加深阴影和加重颜色。缩小位图尺寸也会使原图变形,因为此举是通过减少像素来使整个图像变小的。同样,由于位图图像是以排列的像素集合体形式创建的,所以不能单独操作(如移动)局部位图。
位图文件(Bitmap),扩展名可以是.bmp或者.dib。位图是Windows标准格式图形文件,它将图像定义为由点(像素)组成,每个点可以由多种色彩表示,包括2、4、8、16、24和32位色彩。例如,一幅1024×768分辨率的32位真彩图片,其所占存储字节数为:1024×768×32/(8*1024)=3072KB
)8、结束 over
拖拽视图的创建
public DragView startDrag()
public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY, DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion, float initialDragViewScale) { 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; if (LauncherLog.DEBUG_DRAG) { LauncherLog.d(TAG, "startDrag: dragLayerX = " + dragLayerX + ", dragLayerY = " + dragLayerY + ", dragInfo = " + dragInfo + ", registrationX = " + registrationX + ", registrationY = " + registrationY + ", dragRegion = " + dragRegion); } final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left; final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top; // 记录当前的状态 mDragging = true; mDragObject = new DropTarget.DragObject(); mDragObject.dragComplete = false; mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft); mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop); mDragObject.dragSource = source; mDragObject.dragInfo = dragInfo; // 创建DragView对象 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); dragView.show(mMotionDownX, mMotionDownY); handleMoveEvent(mMotionDownX, mMotionDownY); return dragView; }
public void onDragStart(final DragSource source, Object info, int dragAction) { Log.d(TAG, "onDragStart..."); mIsDragOccuring = true; updateChildrenLayersEnabled(false); mLauncher.lockScreenOrientation();// 锁定屏幕 mLauncher.onInteractionBegin(); setChildrenBackgroundAlphaMultipliers(1f); // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging // 正在拖拽的时候,防止卸载或安装导致快捷图标变化更新数据库的操作 InstallShortcutReceiver.enableInstallQueue(); UninstallShortcutReceiver.enableUninstallQueue(); post(new Runnable() { @Override public void run() { if (mIsDragOccuring) { mDeferRemoveExtraEmptyScreen = false; addExtraEmptyScreenOnDrag();// 添加额外的空白页 } } }); }
// 显示DragView对象(将该DragView添加到DragLayer上) public void show(int touchX, int touchY) { mDragLayer.addView(this); // Start the pick-up animation DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0); lp.width = mBitmap.getWidth(); lp.height = mBitmap.getHeight(); lp.customPosition = true; setLayoutParams(lp); // 设置显示位置 setTranslationX(touchX - mRegistrationX); setTranslationY(touchY - mRegistrationY); if (LauncherLog.DEBUG) { LauncherLog.d(TAG, "show DragView: x = " + lp.x + ", y = " + lp.y + ", width = " + lp.width + ", height = " + lp.height + ", this = " + this); } // Post the animation to skip other expensive work happening on the first frame //动画播放 post(new Runnable() { public void run() { mAnim.start(); } }); }
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]; if (LauncherLog.DEBUG_DRAG) { LauncherLog.d(TAG, "handleMoveEvent: x = " + x + ", y = " + y + ", dragView = " + mDragObject.dragView + ", dragX = " + mDragObject.x + ", dragY = " + mDragObject.y); } checkTouchMove(dropTarget);// 检查拖动时的状态 // Check if we are hovering over the scroll areas mDistanceSinceScroll += Math.sqrt(Math.pow(mLastTouch[0] - x, 2) + Math.pow(mLastTouch[1] - y, 2)); mLastTouch[0] = x; mLastTouch[1] = y; checkScrollState(x, y);// 对拖动时的翻页进行判断处理 }
1、如果软键盘可见那么隐藏
2、调用监听事件
3、根据位置坐标创建拖拽对象
4、反馈触摸
5、显示DragView对象dragView.show()
6、根据当前位置处理移动事件(handleMoveEvent())
三、拦截之后的消费
public boolean onInterceptTouchEvent(MotionEvent ev)
若上述方法返回值为true则表示有触摸事件将该消息传递给onTouchEvent
onTouchEvent ()
用以处理触摸事件。
public boolean onInterceptTouchEvent(MotionEvent ev) { @SuppressWarnings("all") // suppress dead code warning final boolean debug = false; if (debug) { Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging=" + mDragging); } // 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]; if (LauncherLog.DEBUG_MOTION) { LauncherLog.d(TAG, "onInterceptTouchEvent: action = " + action + ", mDragging = " + mDragging + ", dragLayerX = " + dragLayerX + ", dragLayerY = " + dragLayerY); } 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.willAcceptDrop(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; }
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 handleMoveEvent(int x, int y)
其是处理拖拽的主要方法根据被拖拽的对象的释放位置进行判断。
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]; if (LauncherLog.DEBUG_DRAG) { LauncherLog.d(TAG, "handleMoveEvent: x = " + x + ", y = " + y + ", dragView = " + mDragObject.dragView + ", dragX = " + mDragObject.x + ", dragY = " + mDragObject.y); } checkTouchMove(dropTarget);// 检查拖动时的状态 // Check if we are hovering over the scroll areas mDistanceSinceScroll += Math.sqrt(Math.pow(mLastTouch[0] - x, 2) + Math.pow(mLastTouch[1] - y, 2)); mLastTouch[0] = x; mLastTouch[1] = y; checkScrollState(x, y);// 对拖动时的翻页进行判断处理 }
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;// 更新前一次记录的拖拽目的对象为当前拖拽目的对象 }
private void checkScrollState(int x, int y) {// 对拖动时的翻页进行判断处理 final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop(); final int delay = mDistanceSinceScroll < slop ? RESCROLL_DELAY : SCROLL_DELAY; final DragLayer dragLayer = mLauncher.getDragLayer(); final boolean isRtl = (dragLayer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); final int forwardDirection = isRtl ? SCROLL_RIGHT : SCROLL_LEFT; final int backwardsDirection = isRtl ? SCROLL_LEFT : SCROLL_RIGHT; if (x < mScrollZone) { if (mScrollState == SCROLL_OUTSIDE_ZONE) { mScrollState = SCROLL_WAITING_IN_ZONE; if (mDragScroller.onEnterScrollArea(x, y, forwardDirection)) { dragLayer.onEnterScrollArea(forwardDirection); mScrollRunnable.setDirection(forwardDirection); mHandler.postDelayed(mScrollRunnable, delay); } } } else if (x > mScrollView.getWidth() - mScrollZone) { if (mScrollState == SCROLL_OUTSIDE_ZONE) { mScrollState = SCROLL_WAITING_IN_ZONE; if (mDragScroller.onEnterScrollArea(x, y, backwardsDirection)) { dragLayer.onEnterScrollArea(backwardsDirection); mScrollRunnable.setDirection(backwardsDirection); mHandler.postDelayed(mScrollRunnable, delay); } } } else { clearScrollRunnable(); } }
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; }
// 拖拽对象被放置到目标位置 public void onDrop(final DragObject d) { mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView, mDragViewVisualCenter);// 计算拖动View的视觉中心 CellLayout dropTargetLayout = mDropToLayout; // We want the point to be mapped to the dragTarget. // 判断当前是否在Hotseat上,求出相对于dropTargetLayout的视觉中心坐标 if (dropTargetLayout != null) { if (mLauncher.isHotseatLayout(dropTargetLayout)) { mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter); } else { mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null); } } if (LauncherLog.DEBUG_DRAG) { LauncherLog.d(TAG, "onDrop 1: drag view = " + d.dragView + ", dragInfo = " + d.dragInfo + ", dragSource = " + d.dragSource + ", dropTargetLayout = " + dropTargetLayout + ", mDragInfo = " + mDragInfo + ", mInScrollArea = " + mInScrollArea + ", this = " + this); } int snapScreen = -1; boolean resizeOnDrop = false; // 如果DragObject-dragSource!= Worspace,转而调用onDropExternal(),否则继续处理onDrop()的内容 if (d.dragSource != this) { final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1] }; onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d); } else if (mDragInfo != null) { final View cell = mDragInfo.cell; Runnable resizeRunnable = null; if (dropTargetLayout != null && !d.cancelled) { // Move internally boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout); boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout); long container = hasMovedIntoHotseat ? LauncherSettings.Favorites.CONTAINER_HOTSEAT : LauncherSettings.Favorites.CONTAINER_DESKTOP; long screenId = (mTargetCell[0] < 0) ? mDragInfo.screenId : getIdForScreen(dropTargetLayout); int spanX = mDragInfo != null ? mDragInfo.spanX : 1; int spanY = mDragInfo != null ? mDragInfo.spanY : 1; // First we find the cell nearest to point at which the item is // dropped, without any consideration to whether there is an item there. mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell); float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); if (LauncherLog.DEBUG_DRAG) { LauncherLog.d(TAG, "onDrop 2: cell = " + cell + ", screenId = " + screenId + ", mInScrollArea = " + mInScrollArea + ", mTargetCell = " + mTargetCell + ", this = " + this); } // If the item being dropped is a shortcut and the nearest drop // cell also contains a shortcut, then create a folder with the two shortcuts. // 如果拖拽的对象是一个快捷图标并且最近的位置上也是一个快捷图标,就创建一个文件夹来防止这两个图标 if (!mInScrollArea && createUserFolderIfNecessary(cell, container, dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) { return; } if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, distance, d, false)) { return; } // 添加到已存在的文件夹上 // Aside from the special case where we're dropping a shortcut onto a shortcut, // we need to find the nearest cell location that is vacant ItemInfo item = (ItemInfo) d.dragInfo; int minSpanX = item.spanX; int minSpanY = item.spanY; if (item.minSpanX > 0 && item.minSpanY > 0) { minSpanX = item.minSpanX; minSpanY = item.minSpanY; } //如果不满足文件夹的条件,则调用CellLayout-performReorder方法,这个方法就是处理拖动图标时,如果当前落点被占据时,挤开当前图标的效果 int[] resultSpan = new int[2]; mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP); /// M: We think a cell has been found only if the target cell /// and the span are both valid. boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0 && resultSpan[0] > 0 && resultSpan[1] > 0; if (LauncherLog.DEBUG) { LauncherLog.d(TAG, "onDrop 3: foundCell = " + foundCell + "mTargetCell = (" + mTargetCell[0] + ", " + mTargetCell[1] + "), resultSpan = (" + resultSpan[0] + "," + resultSpan[1] + "), item.span = (" + item.spanX + ", " + item.spanY + ") ,item.minSpan = (" + item.minSpanX + ", " + item.minSpanY + "),minSpan = (" + minSpanX + "," + minSpanY + ")."); } // if the widget resizes on drop if (foundCell && (cell instanceof AppWidgetHostView) && (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) { resizeOnDrop = true; item.spanX = resultSpan[0]; item.spanY = resultSpan[1]; AppWidgetHostView awhv = (AppWidgetHostView) cell; AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0], resultSpan[1]);// AppWidget可能在拖动时发生缩小,因此会调用 //AppWidgetResizeFrame-updateWidgetSizeRanges方法 } // 拖动时可能落点在别的页面,所以还会有页面滑动的效果 if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) { snapScreen = getPageIndexForScreenId(screenId); snapToPage(snapScreen); } // 如果满足则更新位置,保存新的位置信息到数据库中,播放动画效果,否则弹回原来位置 if (foundCell) { final ItemInfo info = (ItemInfo) cell.getTag(); if (hasMovedLayouts) { // Reparent the view CellLayout parentCell = getParentCellLayoutForView(cell); if (parentCell != null) { parentCell.removeView(cell); } else if (LauncherAppState.isDogfoodBuild()) { throw new NullPointerException("mDragInfo.cell has null parent"); } addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX, info.spanY);// 更新数据库 } // update the item's position after drop CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); lp.cellX = lp.tmpCellX = mTargetCell[0]; lp.cellY = lp.tmpCellY = mTargetCell[1]; lp.cellHSpan = item.spanX; lp.cellVSpan = item.spanY; lp.isLockedToGrid = true; if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT && cell instanceof LauncherAppWidgetHostView) { final CellLayout cellLayout = dropTargetLayout; // We post this call so that the widget has a chance to be placed // in its final location final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell; AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo(); if (pinfo != null && pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) { final Runnable addResizeFrame = new Runnable() { public void run() { DragLayer dragLayer = mLauncher.getDragLayer(); dragLayer.addResizeFrame(info, hostView, cellLayout); } }; resizeRunnable = (new Runnable() { public void run() { if (!isPageMoving()) { addResizeFrame.run(); } else { mDelayedResizeRunnable = addResizeFrame; } } }); } } LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, lp.cellX, lp.cellY, item.spanX, item.spanY); } else { // If we can't find a drop location, we return the item to its original position CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); mTargetCell[0] = lp.cellX; mTargetCell[1] = lp.cellY; CellLayout layout = (CellLayout) cell.getParent().getParent(); layout.markCellsAsOccupiedForView(cell); } } final CellLayout parent = (CellLayout) cell.getParent().getParent(); final Runnable finalResizeRunnable = resizeRunnable; // Prepare it to be animated into its new position // This must be called after the view has been re-parented final Runnable onCompleteRunnable = new Runnable() { @Override public void run() { mAnimatingViewIntoPlace = false; updateChildrenLayersEnabled(false); if (finalResizeRunnable != null) { finalResizeRunnable.run(); } } }; mAnimatingViewIntoPlace = true; if (d.dragView.hasDrawn()) { final ItemInfo info = (ItemInfo) cell.getTag(); if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) { int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE : ANIMATE_INTO_POSITION_AND_DISAPPEAR; animateWidgetDrop(info, parent, d.dragView, onCompleteRunnable, animationType, cell, false); } else { int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION; mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration, onCompleteRunnable, this); } } else { d.deferDragViewCleanupPostAnimation = false; cell.setVisibility(VISIBLE); } parent.onDropChild(cell); /// M: Call the appropriate callback when don't drop the IMtkWidget. if (mTargetCell[0] == -1 && mTargetCell[1] == -1) { stopDragAppWidget(getPageIndexForScreenId(mDragInfo.screenId)); } } }
拖拽结束 代码全程标有注释 大概如此 我不会告诉你这篇博客我写了差不多一整天 好心累 熟能生巧 继续加油
0 0
- Android学习历程--Launcher拖拽流程
- Android学习历程--Launcher整体加载流程总结
- Launcher App拖拽流程
- android 启动launcher流程
- Android launcher加载流程
- Android launcher加载流程
- Android Framework学习(四)之Launcher启动流程解析
- Android—launcher 启动流程
- Android系统启动流程 七--launcher
- Launcher启动流程&&加载流程学习
- Android---launcher学习
- android Launcher学习总结
- Android Launcher学习
- android Launcher学习总结
- Android Launcher 学习笔记
- android launcher学习点滴
- Android Launcher开发学习--简易Launcher开发
- AndroidICS4.0版本的launcher拖拽的流程
- Android studio真机模拟调试
- java虚拟机-HotSpot的算法实现
- checkbox 全选
- R语言环境在Ubuntu下的安装
- viewpager切换动画
- Android学习历程--Launcher拖拽流程
- 在普通java类里获取Spring管理的bean
- tomcat内存溢出设置JAVA_OPTS
- poi API大全
- MySQL实现的四种通信协议
- web安全颜色以及微软支持颜色
- Java反射-修改String常量
- 苹果手机适配和分辨率
- 利用Jpgraph创建柱状图展示年度收支表