【原始碼剖析】Launcher 8.0 原始碼 26---使用者操作(3)拖拽模式之springload
接下來是第三種狀態,springloader模式,也是drag模式。
到此刻使用者的操作有3中,點選,滑動,長按。
點選是觸發onclick,滑動是GroupView自帶方法,而長按一種是進入overview或allapp模式,另外一種就是接下來學習的drag模式。
之前分析,drag模式的起點有 workspace、folder、allapp、widget、overview。
其中, workspace、folder、allapp、widget都是對item進行操作分別從不同的drag進入到dragControll的startdrag方法。 而overview則是對cellLayout的操作採用startreording方法,
首先看最常用的workspace。
從Launcher的onlongclick方法開始,如果是長按item則會呼叫此方法
mWorkspace.startDrag(longClickCellInfo, new DragOptions());此方法表示workspace上drag的
public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) { View child = cellInfo.cell; // Make sure the drag was started by a long press as opposed to a long click. if (!child.isInTouchMode()) { return; } //啟動drag先將點選的圖示就隱藏起來,對於的該VIew的圖示會顯示在手指按的地方,這樣就給使用者一種圖示被抓起來的感覺。 mDragInfo = cellInfo; child.setVisibility(INVISIBLE);
//看到mDragController就注意,這裡是新增addDragListener。 if (options.isAccessibleDrag) { mDragController.addDragListener(new AccessibleDragListenerAdapter( this, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG) { @Override protected void enableAccessibleDrag(boolean enable) { super.enableAccessibleDrag(enable); setEnableForLayout(mLauncher.getHotseat().getLayout(),enable); setOnClickListener(enable ? null : mLauncher); } }); }
//最重要的是進入beginDragShared,後面是關於指尖圖示繪製的。 beginDragShared(child, this, options); }
//點選應用能獲取應用的所有資訊,長按同樣能,只是根據功能不同,用dragObject來區分一下,child是一個view,而dragObject則是ItemInfo。
public void beginDragShared(View child, DragSource source, DragOptions options) { Object dragObject = child.getTag(); beginDragShared(child, source, (ItemInfo) dragObject, new DragPreviewProvider(child), options); }
// 這個beginDragShared和上面的有所不同,一個是傳入引數不同,另一個是呼叫的物件不用。此處的beginDragShared,在workspace,folder和allapp三種介面均可以被呼叫。
public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions dragOptions) { child.clearFocus(); child.setPressed(false); mOutlineProvider = previewProvider; //手指所在圖示的繪製,該圖示會跟隨手指變動,但是在這裡,只是繪製被點起來的瞬間,則是根據使用者長按的點來決定繪製圖標被抓取的地方。
final Bitmap b = previewProvider.createDragBitmap(mCanvas); int halfPadding = previewProvider.previewPadding / 2; float scale = previewProvider.getScaleAndPosition(b, mTempXY); int dragLayerX = mTempXY[0]; int dragLayerY = mTempXY[1]; DeviceProfile grid = mLauncher.getDeviceProfile(); Point dragVisualizeOffset = null; Rect dragRect = null; if (child instanceof BubbleTextView) { dragRect = new Rect(); ((BubbleTextView) child).getIconBounds(dragRect); dragLayerY += dragRect.top; dragVisualizeOffset = new Point(- halfPadding, halfPadding); } else if (child instanceof FolderIcon) { int previewSize = grid.folderIconSizePx; dragVisualizeOffset = new Point(- halfPadding, halfPadding - child.getPaddingTop()); dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize); } else if (previewProvider instanceof ShortcutDragPreviewProvider) { dragVisualizeOffset = new Point(- halfPadding, halfPadding); } //如果是從allapp長按進來的,會儲存一下mDragSourceInternal if (child.getParent() instanceof ShortcutAndWidgetContainer) { mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent(); } //對popup window進行準備 if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) { PopupContainerWithArrow popupContainer = PopupContainerWithArrow .showForIcon((BubbleTextView) child); if (popupContainer != null) { dragOptions.preDragCondition = popupContainer.createPreDragCondition(); mLauncher.getUserEventDispatcher().resetElapsedContainerMillis(); } } //呼叫關鍵方法mDragController.startDrag DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, dragObject, dragVisualizeOffset, dragRect, scale, dragOptions); dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); b.recycle(); return dv; }
//進入dragController裡面的startDrag,都要走的drag
public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY, DragSource source, ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale, DragOptions options) {
//程式碼首先關閉了輸入法。
mLauncher.getSystemService(InputMethodManager.class) .hideSoftInputFromWindow(mWindowToken, 0); //記錄點選的x值和y值 mOptions = options; if (mOptions.systemDndStartPoint != null) { mMotionDownX = mOptions.systemDndStartPoint.x; mMotionDownY = mOptions.systemDndStartPoint.y; } //根據點選的x值y值以及被點選圖示的長度寬度等,計算出各類需要的引數。 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; mLastDropTarget = null;
//生成DragObject和 dragView,本質都是被drag的內容。 mDragObject = new DropTarget.DragObject(); mIsInPreDrag = mOptions.preDragCondition != null && !mOptions.preDragCondition.shouldStartDrag(0); final Resources res = mLauncher.getResources(); final float scaleDps = mIsInPreDrag ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX, registrationY, initialDragViewScale, scaleDps); mDragObject.dragComplete = false;
//計算圖示應該處於的橫縱座標,相關引數有點多,這是因為參考座標不一樣。首先座標是在整個手機的座標,還是在Launcher的座標,兩者之間相差一個狀態列的高度。其次,座標是通過使用者點選的位置獲取的,但是整個圖示本身有大小,所以手指點選的位置,直接賦值給圖示的話,會有和圖示本身大小相關的偏移。因此,會有較多的計算量。 if (mOptions.isAccessibleDrag) { 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.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView); mDragDriver = DragDriver.create(mLauncher, this, mDragObject, mOptions); } mDragObject.dragSource = source; mDragObject.dragInfo = dragInfo; mDragObject.originalDragInfo = new ItemInfo(); mDragObject.originalDragInfo.copyFrom(dragInfo);
mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); dragView.show(mMotionDownX, mMotionDownY); mDistanceSinceScroll = 0; //設定好所需的引數就開始通知其他模組,callOnDragStart()對應監聽器,很多模組都在監聽drag的動作,如果當前處於此drag狀態,其他模組也會隨之而動。比如,buttondroptarget模組。 if (!mIsInPreDrag) { callOnDragStart(); } else if (mOptions.preDragCondition != null) { mOptions.preDragCondition.onPreDragStart(mDragObject); } //而後呼叫handleMoveEvent方法。 mLastTouch[0] = mMotionDownX; mLastTouch[1] = mMotionDownY; handleMoveEvent(mMotionDownX, mMotionDownY); mLauncher.getUserEventDispatcher().resetActionDurationMillis(); return dragView; }
handleMoveEvent是處理drag在移動過程的方法,startdrag也屬於移動的開始,所以也呼叫本方法,而本方法會在drag的過程中持續被呼叫。在onstart的時候傳入的時候長按時候的手指位置,而後面在手指移動時,會不停傳入手指的實時位置。
private void handleMoveEvent(int x, int y) { //進入方法,首先將dragview移動到手指下方。
mDragObject.dragView.move(x, y);
// checkTouchMove是確定當前drop的物件。這裡drop理解為,如果鬆手,圖示會放到哪裡,具體方法,見後面。 final int[] coordinates = mCoordinatesTemp; DropTarget dropTarget = findDropTarget(x, y, coordinates); mDragObject.x = coordinates[0]; mDragObject.y = coordinates[1]; checkTouchMove(dropTarget);
//如有需要,告訴大家開始drag了
if (mIsInPreDrag && mOptions.preDragCondition != null && mOptions.preDragCondition.shouldStartDrag(mDistanceSinceScroll)) { callOnDragStart(); } }
dropTarget是一個介面,實現了這個介面的有workspace、folder、ButtonDropTarget、fodlerIcon。其中ButtonDropTarget是drag時出現在頂部的刪除解除安裝等選項。
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留到後面統一學習。
Workspace的drag開頭,也就是進入springload模式就算是完成了。流程是隱藏workspace的圖示,在手指出繪製圖標,然後進圖drag移動的模式。