在系统Launcher3中 第一屏默认会放一个时钟AppWidget,对于时钟布局是可以任意拖动的,也是可以任意拖动到其他屏,拖动到其他屏显得很不好看,所以根据需求改成不能拖动时钟去其他屏,这就需要分析拖拽整个流程,根据包名获取如果是时钟就静止拖拽就可以了
packages/apps/Launcher3/src/com/android/launcher3/Workspace.java
packages/apps/Launcher3/src/com/android/launcher3/DropTarget.java
public interface DropTarget {
class DragObject {
boolean isDropEnabled();
/**
* Handle an object being dropped on the DropTarget.
*
* This will be called only if this target previously returned true for {@link #acceptDrop}. It
* is the responsibility of this target to exit out of the spring loaded mode (either
* immediately or after any pending animations).
*
* If the drop was cancelled for some reason, onDrop will never get called, the UI will
* automatically exit out of this mode.
*/
void onDrop(DragObject dragObject, DragOptions options);
void onDragEnter(DragObject dragObject);
void onDragOver(DragObject dragObject);
void onDragExit(DragObject dragObject);
/**
* Check if a drop action can occur at, or near, the requested location.
* This will be called just before onDrop.
* @return True if the drop will be accepted, false otherwise.
*/
boolean acceptDrop(DragObject dragObject);
void prepareAccessibilityDrop();
// These methods are implemented in Views
void getHitRectRelativeToDragLayer(Rect outRect);
}
从代码中可以看到onDrop()拖拽停止后执行的动作 onDragEnter(拖拽中onDragOver(拖拽结束
onDragExit(退出拖拽的,整个拖拽完成执行相关功能代码
workSpace是实现了DropTarget的接口类,所以会对整个拖拽过程有详细的执行过程,来分析
拖拽过程执行相关的代码就可以实现这个功能
首选分析Workspace.java 看拖拽流程
packages/apps/Launcher3/src/com/android/launcher3/Workspace.java
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
if (TestProtocol.sDebugTracing) {
android.util.Log.d(TestProtocol.NO_DRAG_TAG,
"onDragStart 1");
}
if (ENFORCE_DRAG_EVENT_ORDER) {
enforceDragParity("onDragStart", 0, 0);
}
if (mDragInfo != null && mDragInfo.cell != null && mDragInfo.cell.getParent() != null) {
CellLayout layout = (CellLayout) mDragInfo.cell.getParent().getParent();
if (layout != null) layout.markCellsAsUnoccupiedForView(mDragInfo.cell);
}
if (mOutlineProvider != null) {
if (dragObject.dragView != null) {
Bitmap preview = dragObject.dragView.getPreviewBitmap();
// The outline is used to visualize where the item will land if dropped
mOutlineProvider.generateDragOutline(preview);
}
}
updateChildrenLayersEnabled();
// Do not add a new page if it is a accessible drag which was not started by the workspace.
// We do not support accessibility drag from other sources and instead provide a direct
// action for move/add to homescreen.
// When a accessible drag is started by the folder, we only allow rearranging withing the
// folder.
boolean addNewPage = !(options.isAccessibleDrag && dragObject.dragSource != this);
if (addNewPage) {
mDeferRemoveExtraEmptyScreen = false;
addExtraEmptyScreenOnDrag();
if (dragObject.dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
&& dragObject.dragSource != this) {
// When dragging a widget from different source, move to a page which has
// enough space to place this widget (after rearranging/resizing). We special case
// widgets as they cannot be placed inside a folder.
// Start at the current page and search right (on LTR) until finding a page with
// enough space. Since an empty screen is the furthest right, a page must be found.
int currentPage = getPageNearestToCenterOfScreen();
for (int pageIndex = currentPage; pageIndex < getPageCount(); pageIndex++) {
CellLayout page = (CellLayout) getPageAt(pageIndex);
if (page.hasReorderSolution(dragObject.dragInfo)) {
setCurrentPage(pageIndex);
break;
}
}
}
}
// Always enter the spring loaded mode
if (TestProtocol.sDebugTracing) {
android.util.Log.d(TestProtocol.NO_DRAG_TAG,
"onDragStart 2");
}
mLauncher.getStateManager().goToState(SPRING_LOADED);
}
从上述代码发现onDragStart() 处理的是开始拖拽的事件,当长按图标的时候开始执行初始化拖拽的相关代码,做好准备工作而
onDrop() 处理的是拖拽后的事件处理,获取当前拖拽的对象,以及要拖拽到哪里去,然后做出相关的处理
public void onDrop(final DragObject d, DragOptions options) {
mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
CellLayout dropTargetLayout = mDropToLayout;
// We want the point to be mapped to the dragTarget.
if (dropTargetLayout != null) {
mapPointFromDropLayout(dropTargetLayout, mDragViewVisualCenter);
}
boolean droppedOnOriginalCell = false;
int snapScreen = -1;
boolean resizeOnDrop = false;
if (d.dragSource != this || mDragInfo == null) {
final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1] };
onDropExternal(touchXY, dropTargetLayout, d);
} else {
AbstractFloatingView.closeOpenViews(mLauncher, false, AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME);
final View cell = mDragInfo.cell;
boolean droppedOnOriginalCellDuringTransition = false;
Runnable onCompleteRunnable = null;
//DragObject对象是对拖拽对象的封装 dragInfo是他的一些详细信息,而包名类名也是可以通过 providerName来获取的
//只要获取到包名的相关信息 可以对包名进行判断就可以了 ,如果是这个包名就不会移动到其他屏,当拖动放开后会回到以前的位置
// add code start
ItemInfo iteminfo = d.dragInfo;
String packagename = "";
if(iteminfo!=null && iteminfo instanceof LauncherAppWidgetInfo){
ComponentName componentName = ((LauncherAppWidgetInfo)iteminfo).providerName;
if(componentName!=null){
packagename = componentName.getPackageName();
}
}
// add code end
- if (dropTargetLayout != null && !d.cancelled ) {
+ if (dropTargetLayout != null && !d.cancelled && !packagename.equals("com.android.deskclock")) {
// Move internally
boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
int container = hasMovedIntoHotseat ?
LauncherSettings.Favorites.CONTAINER_HOTSEAT :
LauncherSettings.Favorites.CONTAINER_DESKTOP;
int 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 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 (createUserFolderIfNecessary(cell, container,
dropTargetLayout, mTargetCell, distance, false, d.dragView) ||
addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
distance, d, false)) {
mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
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 = d.dragInfo;
int minSpanX = item.spanX;
int minSpanY = item.spanY;
if (item.minSpanX > 0 && item.minSpanY > 0) {
minSpanX = item.minSpanX;
minSpanY = item.minSpanY;
}
droppedOnOriginalCell = item.screenId == screenId && item.container == container
&& item.cellX == mTargetCell[0] && item.cellY == mTargetCell[1];
droppedOnOriginalCellDuringTransition = droppedOnOriginalCell && mIsSwitchingState;
// When quickly moving an item, a user may accidentally rearrange their
// workspace. So instead we move the icon back safely to its original position.
boolean returnToOriginalCellToPreventShuffling = !isFinishedSwitchingState()
&& !droppedOnOriginalCellDuringTransition && !dropTargetLayout
.isRegionVacant(mTargetCell[0], mTargetCell[1], spanX, spanY);
int[] resultSpan = new int[2];
if (returnToOriginalCellToPreventShuffling) {
mTargetCell[0] = mTargetCell[1] = -1;
} else {
mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
}
boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
// 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]);
}
if (foundCell) {
if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
snapScreen = getPageIndexForScreenId(screenId);
snapToPage(snapScreen);
}
final ItemInfo info = (ItemInfo) cell.getTag();
if (hasMovedLayouts) {
// Reparent the view
CellLayout parentCell = getParentCellLayoutForView(cell);
if (parentCell != null) {
parentCell.removeView(cell);
} else if (FeatureFlags.IS_DOGFOOD_BUILD) {
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
&& !d.accessibleDrag) {
onCompleteRunnable = new Runnable() {
public void run() {
if (!isPageInTransition()) {
AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
}
}
};
}
}
mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
lp.cellX, lp.cellY, item.spanX, item.spanY);
} else {
if (!returnToOriginalCellToPreventShuffling) {
onNoCellFound(dropTargetLayout);
}
// 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;
if (cell.getParent() != null) {
CellLayout layout = (CellLayout) cell.getParent().getParent();
layout.markCellsAsOccupiedForView(cell);
}
}
}
final CellLayout parent =
cell.getParent() == null ? null : (CellLayout) cell.getParent().getParent();
if (d.dragView.hasDrawn()) {
if (droppedOnOriginalCellDuringTransition) {
// Animate the item to its original position, while simultaneously exiting
// spring-loaded mode so the page meets the icon where it was picked up.
if (cell.getParent() != null) {
mLauncher.getDragController().animateDragViewToOriginalPosition(
onCompleteRunnable, cell, SPRING_LOADED_TRANSITION_MS);
} else {
resetDragCellIfNeed(mDragInfo);
}
mLauncher.getStateManager().goToState(NORMAL);
mLauncher.getDropTargetBar().onDragEnd();
if (parent != null) {
parent.onDropChild(cell);
}
return;
}
final ItemInfo info = (ItemInfo) cell.getTag();
boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
|| info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
if (isWidget) {
int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
ANIMATE_INTO_POSITION_AND_DISAPPEAR;
if (parent != null) {
animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false);
}
} else {
int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
if (cell.getParent() != null) {
mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
this);
} else {
d.deferDragViewCleanupPostAnimation = false;
resetDragCellIfNeed(mDragInfo);
}
}
} else {
d.deferDragViewCleanupPostAnimation = false;
if (cell.getParent() != null) {
cell.setVisibility(VISIBLE);
} else {
resetDragCellIfNeed(mDragInfo);
}
}
if (parent != null) {
parent.onDropChild(cell);
}
mLauncher.getStateManager().goToState(
NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable);
}
if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
d.stateAnnouncer.completeAction(R.string.item_moved);
}
}
可以发现在onDrop()中可以通过DragObject对象获取拖拽的包名,然后对包名进行判断,如果是时钟就排除在外就可以实现功能了