1. 程式人生 > >【原始碼剖析】Launcher 8.0 原始碼 26---使用者操作(3)拖拽模式之springload

【原始碼剖析】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移動的模式。