LauncherDragController,继承了DragController
- DragListener集合,回调对应的onDragStart()、onDragEnd()方法,Workspace,DropTargetBar和它的两个子View,DeleteDropTarget、SecondaryDropTarget,Folder、PopupContainerWithArrow、应用列表都会被添加到这个集合
- 构建LauncherDragView,添加到DragLayer,用来显示应用图标,并调用它移动方法进行移动
- 构建PopupContainerWithArrow控件,用来弹窗显示应用信息
LauncherDragView,继承DragView
长按显示的图标控件和进行移动
添加控件到页面的时候,会设置长按事件
WorkspaceLayoutManager
default void addInScreen(View child, int container, int screenId, int x, int y,
int spanX, int spanY) {
...
//添加到页面
if (!layout.addViewToCellLayout(child, -1, childId, lp, markCellsAsOccupied)) {
// TODO: This branch occurs when the workspace is adding views
// outside of the defined grid
// maybe we should be deleting these items from the LauncherModel?
Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
}
child.setHapticFeedbackEnabled(false);
//这里设置了长按事件
child.setOnLongClickListener(getWorkspaceChildOnLongClickListener());
if (child instanceof DropTarget) {
//添加到LauncherDragController的mDropTargets集合
onAddDropTarget((DropTarget) child);
}
}
default View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
return ItemLongClickListener.INSTANCE_WORKSPACE;
}
ItemLongClickListener
//静态方法
private static boolean onWorkspaceItemLongClick(View v) {
if (v instanceof LauncherAppWidgetHostView) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
} else {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onWorkspaceItemLongClick");
}
Launcher launcher = Launcher.getLauncher(v.getContext());
if (!canStartDrag(launcher)) return false;
if (!launcher.isInState(NORMAL) && !launcher.isInState(OVERVIEW)) return false;
if (!(v.getTag() instanceof ItemInfo)) return false;
launcher.setWaitingForResult(null);
beginDrag(v, launcher, (ItemInfo) v.getTag(), launcher.getDefaultWorkspaceDragOptions());
return true;
}
public static void beginDrag(View v, Launcher launcher, ItemInfo info,
DragOptions dragOptions) {
//大于0,表示是文件夹的数据
if (info.container >= 0) {
//通过DragLayer遍历子View获取Folder控件,Folder继承自AbstractFloatingView
Folder folder = Folder.getOpen(launcher);
if (folder != null) {
if (!folder.getIconsInReadingOrder().contains(v)) {
folder.close(true);
} else {
folder.startDrag(v, dragOptions);
return;
}
}
}
CellLayout.CellInfo longClickCellInfo = new CellLayout.CellInfo(v, info);
//调用Workspace的startDrag()方法
launcher.getWorkspace().startDrag(longClickCellInfo, dragOptions);
}
Workspace
public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
View child = cellInfo.cell;
mDragInfo = cellInfo;
//控件设置不可见
child.setVisibility(INVISIBLE);
//DragOptions默认为false
if (options.isAccessibleDrag) {
mDragController.addDragListener(
new AccessibleDragListenerAdapter(this, WorkspaceAccessibilityHelper::new) {
@Override
protected void enableAccessibleDrag(boolean enable) {
super.enableAccessibleDrag(enable);
setEnableForLayout(mLauncher.getHotseat(), enable);
}
});
}
beginDragShared(child, this, options);
}
beginDragShared()
/**
* Core functionality for beginning a drag operation for an item that will be dropped within
* the workspace
*/
public DragView beginDragShared(View child, DraggableView draggableView, DragSource source,
ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions dragOptions) {
float iconScale = 1f;
if (child instanceof BubbleTextView) {
Drawable icon = ((BubbleTextView) child).getIcon();
//BubbleTextView的icon是FastBitmapDrawable
if (icon instanceof FastBitmapDrawable) {
iconScale = ((FastBitmapDrawable) icon).getAnimatedScale();
}
}
// Clear the pressed state if necessary
child.clearFocus();
child.setPressed(false);
if (child instanceof BubbleTextView) {
BubbleTextView icon = (BubbleTextView) child;
icon.clearPressedBackground();
}
//draggableView传进来是空,BubbleTextView实现了DraggableView接口
if (draggableView == null && child instanceof DraggableView) {
draggableView = (DraggableView) child;
}
//如果child不是LauncherAppWidgetHostView控件,则contentView为null
final View contentView = previewProvider.getContentView();
final float scale;
// The draggable drawable follows the touch point around on the screen
final Drawable drawable;
if (contentView == null) {
drawable = previewProvider.createDrawable();
scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
} else {
drawable = null;
scale = previewProvider.getScaleAndPosition(contentView, mTempXY);
}
int halfPadding = previewProvider.previewPadding / 2;
int dragLayerX = mTempXY[0];
int dragLayerY = mTempXY[1];
Point dragVisualizeOffset = null;
Rect dragRect = new Rect();
if (draggableView != null) {
////如果是BuddleTextView,则把图标的大小赋值给dragRect矩形
draggableView.getSourceVisualDragBounds(dragRect);
dragLayerY += dragRect.top;
dragVisualizeOffset = new Point(- halfPadding, halfPadding);
}
if (child.getParent() instanceof ShortcutAndWidgetContainer) {
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
}
if (child instanceof BubbleTextView) {
BubbleTextView btv = (BubbleTextView) child;
if (!dragOptions.isAccessibleDrag) {
//startLongPressAction()方法构建PopupContainerWithArrow,也就是显示应用信息的弹窗
dragOptions.preDragCondition = btv.startLongPressAction();
}
if (btv.isDisplaySearchResult()) {
dragOptions.preDragEndScale = (float) mAllAppsIconSize / btv.getIconSize();
}
}
final DragView dv;
if (contentView instanceof View) {
if (contentView instanceof LauncherAppWidgetHostView) {
mDragController.addDragListener(new AppWidgetHostViewDragListener(mLauncher));
}
dv = mDragController.startDrag(
contentView,
draggableView,
dragLayerX,
dragLayerY,
source,
dragObject,
dragVisualizeOffset,
dragRect,
scale * iconScale,
scale,
dragOptions);
} else {
//调用LauncherDragController的startDrag()方法
dv = mDragController.startDrag(
drawable,
draggableView,
dragLayerX,
dragLayerY,
source,
dragObject,
dragVisualizeOffset,
dragRect,
scale * iconScale,
scale,
dragOptions);
}
return dv;
}
如果是BubbleTextView控件,通过调用BubbleTextView.startLongPressAction()方法构建PopupContainerWithArrow控件,也就是用来显示应用信息的弹窗
调用LauncherDragController的startDrag()方法
LauncherDragController,继承自DragController
@Override
protected DragView startDrag(
@Nullable Drawable drawable,
@Nullable View view,
DraggableView originalView,
int dragLayerX,
int dragLayerY,
DragSource source,
ItemInfo dragInfo,
Point dragOffset,
Rect dragRegion,
float initialDragViewScale,
float dragViewScaleOnDrop,
DragOptions options) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_DROP_TARGET, "5");
}
if (PROFILE_DRAWING_DURING_DRAG) {
android.os.Debug.startMethodTracing("Launcher");
}
mActivity.hideKeyboard();
//通过DragLayer遍历找到AbstractFloatingView控件,并关闭
//Folder、PopupContainerWithArrow都继承了AbstractFloatingView
AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_DISCOVERY_BOUNCE);
mOptions = options;
//默认为空
if (mOptions.simulatedDndStartPoint != null) {
mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
}
final int registrationX = mMotionDown.x - dragLayerX;
final int registrationY = mMotionDown.y - dragLayerY;
final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
mLastDropTarget = null;
//构建DragObject对象
mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext());
mDragObject.originalView = originalView;
//BuddleTextVie这里不为空,由PopupContainerWithArrow的createPreDragCondition()创建
//如果是PopupContainerWithArrow中的,那么mIsInPreDrag 为true
mIsInPreDrag = mOptions.preDragCondition != null
&& !mOptions.preDragCondition.shouldStartDrag(0);
final Resources res = mActivity.getResources();
final float scaleDps = mIsInPreDrag
? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
//构建LauncherDragView对象
//构造的同时,创建一个ImageView,并添加到自身,并配置缩放动画
final DragView dragView = mDragObject.dragView = drawable != null
? new LauncherDragView(
mActivity,
drawable,
registrationX,
registrationY,
initialDragViewScale,
dragViewScaleOnDrop,
scaleDps)
: new LauncherDragView(
mActivity,
view,
view.getMeasuredWidth(),
view.getMeasuredHeight(),
registrationX,
registrationY,
initialDragViewScale,
dragViewScaleOnDrop,
scaleDps);
dragView.setItemInfo(dragInfo);
mDragObject.dragComplete = false;
mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
mDragDriver = DragDriver.create(this, mOptions, mFlingToDeleteHelper::recordMotionEvent);
if (!mOptions.isAccessibleDrag) {
mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
}
mDragObject.dragSource = source;
mDragObject.dragInfo = dragInfo;
mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
if (dragOffset != null) {
dragView.setDragVisualizeOffset(new Point(dragOffset));
}
if (dragRegion != null) {
dragView.setDragRegion(new Rect(dragRegion));
}
mActivity.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
//把LauncherDragView添加到DragLayer指定位置,并执行动画
dragView.show(mLastTouch.x, mLastTouch.y);
mDistanceSinceScroll = 0;
if (!mIsInPreDrag) {
callOnDragStart();
} else if (mOptions.preDragCondition != null) {
mOptions.preDragCondition.onPreDragStart(mDragObject);
}
handleMoveEvent(mLastTouch.x, mLastTouch.y);
if (!mActivity.isTouchInProgress() && options.simulatedDndStartPoint == null) {
// If it is an internal drag and the touch is already complete, cancel immediately
MAIN_EXECUTOR.submit(this::cancelDrag);
}
return dragView;
}
LauncherDragController的startDrag()方法
- 构建DragObject对象
- 构建LauncherDragView对象,构造的同时,创建一个ImageView,并添加到自身,并配置缩放动画
- 调用LauncherDragView的show()进行显示,会把LauncherDragView添加到DragLayer控件中,并执行动画
- 调用handleMoveEvent()方法
LauncherDragView,继承DragView
public void show(int touchX, int touchY) {
//Launcher.xml中的DragLayer控件添加LauncherDragView
mDragLayer.addView(this);
// Start the pick-up animation
BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(mWidth, mHeight);
lp.customPosition = true;
setLayoutParams(lp);
//mContent 是ImageView
if (mContent != null) {
// At the drag start, the source view visibility is set to invisible.
mContent.setVisibility(VISIBLE);
}
//移动到触摸位置
move(touchX, touchY);
// Post the animation to skip other expensive work happening on the first frame
//触发动画执行
post(mAnim::start);
}
public void move(int touchX, int touchY) {
if (touchX > 0 && touchY > 0 && mLastTouchX > 0 && mLastTouchY > 0
&& mScaledMaskPath != null) {
mTranslateX.animateToPos(mLastTouchX - touchX);
mTranslateY.animateToPos(mLastTouchY - touchY);
}
mLastTouchX = touchX;
mLastTouchY = touchY;
applyTranslation();
}
回到handleMoveEvent()方法
protected void handleMoveEvent(int x, int y) {
//LauncherDragView进行移动
mDragObject.dragView.move(x, y);
// Drop on someone?
final int[] coordinates = mCoordinatesTemp;
//根据x/y坐标找到要移动到的目标控件
//Workspace、Folder、DeleteDropTarget、SecondaryDropTarget都实现了DropTarget接口
DropTarget dropTarget = findDropTarget(x, y, coordinates);
mDragObject.x = coordinates[0];
mDragObject.y = coordinates[1];
//回调dropTarget相应接口,进入、退出等
checkTouchMove(dropTarget);
// Check if we are hovering over the scroll areas
mDistanceSinceScroll += Math.hypot(mLastTouch.x - x, mLastTouch.y - y);
mLastTouch.set(x, y);
int distanceDragged = mDistanceSinceScroll;
if (ATLEAST_Q && mLastTouchClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS) {
distanceDragged /= DEEP_PRESS_DISTANCE_FACTOR;
}
if (mIsInPreDrag && mOptions.preDragCondition != null
&& mOptions.preDragCondition.shouldStartDrag(distanceDragged)) {
callOnDragStart();
}
}
private void checkTouchMove(DropTarget dropTarget) {
if (dropTarget != null) {
if (mLastDropTarget != dropTarget) {
if (mLastDropTarget != null) {
//退出上一个DropTarget
mLastDropTarget.onDragExit(mDragObject);
}
//进入目标DropTarget
dropTarget.onDragEnter(mDragObject);
}
//还在DropTarget内移动
dropTarget.onDragOver(mDragObject);
} else {
if (mLastDropTarget != null) {
mLastDropTarget.onDragExit(mDragObject);
}
}
mLastDropTarget = dropTarget;
}
private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
mDragObject.x = x;
mDragObject.y = y;
final Rect r = mRectTemp;
//Workspace页面在添加子View的时候,发现实现了DropTarget接口,会添加到mDropTargets集合
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);
if (r.contains(x, y)) {
dropCoordinates[0] = x;
dropCoordinates[1] = y;
mActivity.getDragLayer().mapCoordInSelfToDescendant((View) target, dropCoordinates);
return target;
}
}
// Pass all unhandled drag to workspace. Workspace finds the correct
// cell layout to drop to in the existing drag/drop logic.
dropCoordinates[0] = x;
dropCoordinates[1] = y;
//上面如果没有,直接返回的是Workspace
return getDefaultDropTarget(dropCoordinates);
}
//LauncherDragController
@Override
protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
mActivity.getDragLayer().mapCoordInSelfToDescendant(mActivity.getWorkspace(),
dropCoordinates);
return mActivity.getWorkspace();
}
protected void callOnDragStart() {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_DROP_TARGET, "6");
}
if (mOptions.preDragCondition != null) {
mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
}
mIsInPreDrag = false;
if (mOptions.preDragEndScale != 0) {
mDragObject.dragView
.animate()
.scaleX(mOptions.preDragEndScale)
.scaleY(mOptions.preDragEndScale)
.setInterpolator(Interpolators.EMPHASIZED)
.setDuration(DRAG_VIEW_SCALE_DURATION_MS)
.start();
}
//执行LauncherDragView的onDragStart()回调方法
mDragObject.dragView.onDragStart();
//mListeners集合有Workspace、DropTargetBar和它的两个子View,DeleteDropTarget、SecondaryDropTarget
//Folder、PopupContainerWithArrow、应用列表
for (DragListener listener : new ArrayList<>(mListeners)) {
listener.onDragStart(mDragObject, mOptions);
}
}
handleMoveEvent()方法逻辑
- LauncherDragView进行移动,根据坐标找到对应DropTarget实现类,并根据情况调用对应的onDragEnter()、onDragExit()等
- 回调用LauncherDragView的onDragStart()方法,回调DragListener集合的onDragStart()方法,Workspace、DeleteDropTarget等都实现了DragListener 接口,并被添加到这个集合
归纳
长按事件会调用到Workspace的startDrag()方法,如果是应用图标的长按事件,则会创建PopupContainerWithArrow控件,用来负责弹窗显示应用信息,接着调用LauncherDragController的startDrag()方法
在startDarg()方法中,构建DragObject、LauncherDragView对象,LauncherDragView在构造方法中创建一个ImageView,并添加到自身,并配置动画,接着LauncherDragView的show()方法,把LauncherDragView添加到DragLayer控件中,并执行动画
接着调用handleMoveEvent()方法,会调用到LauncherDragView的move()进行移动,根据坐标找到对应DropTarget实现类,并根据情况调用对应的onDragEnter()、onDragExit()等,DropTarget也就是图标移动到的目标对象
接着回调DragListener集合的onDragStart()方法,Workspace、DeleteDropTarget等都实现了DragListener 接口,并被添加到这个集合,DragListener 接口提供了onDragStart()、onDragEnd()方法