长按事件分析
WorkspaceLayoutManager
default void addInScreen(View child, int container, int screenId, int x, int y,
int spanX, int spanY) {
if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
if (getScreenWithId(screenId) == null) {
Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
// DEBUGGING - Print out the stack trace to see where we are adding from
new Throwable().printStackTrace();
return;
}
}
if (EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) {
// This should never happen
throw new RuntimeException("Screen id should not be extra empty screen: " + screenId);
}
final CellLayout layout;
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
|| container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
layout = getHotseat();
// Hide folder title in the hotseat
if (child instanceof FolderIcon) {
((FolderIcon) child).setTextVisible(false);
}
} else {
// Show folder title if not in the hotseat
if (child instanceof FolderIcon) {
((FolderIcon) child).setTextVisible(true);
}
layout = getScreenWithId(screenId);
}
ViewGroup.LayoutParams genericLp = child.getLayoutParams();
CellLayoutLayoutParams lp;
if (genericLp == null || !(genericLp instanceof CellLayoutLayoutParams)) {
lp = new CellLayoutLayoutParams(x, y, spanX, spanY, screenId);
} else {
lp = (CellLayoutLayoutParams) genericLp;
lp.cellX = x;
lp.cellY = y;
lp.cellHSpan = spanX;
lp.cellVSpan = spanY;
}
if (spanX < 0 && spanY < 0) {
lp.isLockedToGrid = false;
}
// Get the canonical child id to uniquely represent this view in this screen
ItemInfo info = (ItemInfo) child.getTag();
int childId = info.getViewId();
boolean markCellsAsOccupied = !(child instanceof Folder);
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) {
onAddDropTarget((DropTarget) child);
}
}
default View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
return ItemLongClickListener.INSTANCE_WORKSPACE;
}
这里给View添加一个动画效果
private static boolean onWorkspaceItemLongClick(View v) {
if (v instanceof LauncherAppWidgetHostView) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
} else {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onWorkspaceItemLongClick");
}
//添加抖动动画
addCusShakeAnimate(v);
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;
}
private static void addCusShakeAnimate(View view) {
TranslateAnimation leftBottomTranslate = new TranslateAnimation(0, -20, 0, -20);
leftBottomTranslate.setDuration(200);
leftBottomTranslate.setRepeatCount(3);
leftBottomTranslate.setRepeatMode(Animation.REVERSE);
TranslateAnimation leftTopTranslate = new TranslateAnimation(0, -20, 0, 20);
leftTopTranslate.setDuration(200);
leftTopTranslate.setRepeatCount(3);
leftTopTranslate.setRepeatMode(Animation.REVERSE);
TranslateAnimation rightTopTranslate = new TranslateAnimation(0, 20, 0, 20);
rightTopTranslate.setDuration(200);
rightTopTranslate.setRepeatCount(3);
rightTopTranslate.setRepeatMode(Animation.REVERSE);
AnimationSet shakeAnimationSet = new AnimationSet(false);
shakeAnimationSet.addAnimation(leftBottomTranslate);
view.setAnimation(shakeAnimationSet);
leftBottomTranslate.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
view.startAnimation(leftTopTranslate);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
leftTopTranslate.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
view.startAnimation(rightTopTranslate);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
rightTopTranslate.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
view.startAnimation(leftBottomTranslate);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
view.startAnimation(shakeAnimationSet);
}
public static void beginDrag(View v, Launcher launcher, ItemInfo info,
DragOptions dragOptions) {
if (info.container >= 0) {
Folder folder = Folder.getOpen(launcher);
if (folder != null) {
if (!folder.getIconsInReadingOrder().contains(v)) {
folder.close(true);
} else {
folder.startDrag(v, dragOptions);
return;
}
}
}
//把当前抖动的View赋值给DragController,开始移动的时候清除动画
DragController dragController = launcher.getDragController();
dragController.setCurShakeView(v);
CellLayout.CellInfo longClickCellInfo = new CellLayout.CellInfo(v, info);
launcher.getWorkspace().startDrag(longClickCellInfo, dragOptions);
}
beginDrag()方法通过startDrag()方法一直调用到DragController的startDrag()方法
DragController是一个抽象类,Launcher这里初始化的是LauncherDragController 类
public abstract class DragController<T extends ActivityContext>
implements DragDriver.EventListener, TouchController {
public class LauncherDragController extends DragController<Launcher> {
Laucher
protected void initDragController() {
mDragController = new LauncherDragController(this);
}
LauncherDragController 的startDrag()会调用到handleMoveEvent()方法,这里处理移动时清除动画
private View mCurShakeView;
private int mLastDragX = -1;
private int mLastDragY = -1;
protected void handleMoveEvent(int x, int y) {
mDragObject.dragView.move(x, y);
//一有移动清除动画
if(mLastDragX != -1 && mLastDragY != -1){
if(Math.abs(mLastDragX - x) > 10 || Math.abs(mLastDragY - y) > 10){
if(null != mCurShakeView){
mCurShakeView.clearAnimation();
}
}
}
mLastDragX = x;
mLastDragY = y;
// Drop on someone?
final int[] coordinates = mCoordinatesTemp;
DropTarget dropTarget = findDropTarget(x, y, coordinates);
mDragObject.x = coordinates[0];
mDragObject.y = coordinates[1];
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();
}
}
效果