前言
在手机屏幕上通过长按动作可以对图标拖拽删除,我们有了多个问题:
- Android 是怎样找到我们所长按的那个图标的?
- 进行拖拽伴随着图标的震动和动画是怎样实现的?
分析代码
我们知道长按屏幕是在 WorkSpace 进行操作的,所以我们直接在这个类中处理长按的方法。
@Override
public void performLongClick(final float motionX, final float motionY) {
// 非滑动状态且找到了对应的 cell
Log.i("检测长按",findTouchCellInfo(motionX, motionY)+" ");
if (!mDraging && findTouchCellInfo(motionX, motionY)) {
//添加一屏
addCellLayout();
//遍历所有 CellLayout 启动动画
startChildShakeAnim();
//找到点击位置所在的 cellInfo
findDragTouchCellInfo(motionX, motionY);
//Toast.makeText(getContext(),"长按",Toast.LENGTH_SHORT).show();
}
}
其中 findDragTouchCellInfo(motionX, motionY) 处理了拖拽图标找到点击位置所在的 cellInfo,我们继续追踪。
在该方法中对应用图标和文件夹分类处理,调用 startDraw () 开始处理拖拽,
在DragController中,startDrag是个多态方法,但最终,都走到了以下这个实现中。
public void startDrag(Bitmap b, int dragLayerX, int dragLayerY, DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion)
handleMoveEvent 是拖拽的主要方法,当用户触发拖拽后,DragController将通过该方法移动被拖拽物视图,并通知各个释放目的对象相应状态的改变。若进入滑屏区域且允许滑屏,执行相应的滑屏操作。
DragController 拖拽控制流程总结
方法流程:(Launcher.onLongClick()-Workspace.beginDragShared()/AppsCustomizePagedView.beginDragging() -> )startDrag() -> onInterceptTouchEvent()/onTouchEvent() -> handleMoveEvent() -> drop()
总的来说,DragController 拖拽控制就是(在进入拖拽之前的步骤见2触发拖拽):
- 进入拖拽:使用 startDrag() 进入拖拽状态;
- 响应触屏:使用 onInterceptTouchEvent() 与 onTouchEvent() 响应用户的触屏动作;
- 处理拖拽:使用 handleMoveEvent() 处理被拖拽物的移动;
- 释放拖拽:使用 drop() 将被拖拽物释放到相应位置。
Android 是怎样找到我们长按的那个图标的?
坐标,在 WorkSpace 中可以找到 findDragTouchCellInfo 方法 , 这个方法将长按处理分成两类,一类是 HotSeat ,另一类是 CellLayout 。
使用 setTagToCellInfoForPoint(x,y)遍历查找坐标所对应的元素
/**
* 设置点击目标TAG 查找X,Y是否在那个目标上
*
* @param touchX
* @param touchY
*/
public boolean setTagToCellInfoForPoint(int touchX, int touchY) {
final CellInfo cellInfo = mCellInfo;
Rect frame = mRect;
final int x = touchX + getScrollX();
final int y = touchY + getScrollY();
final int count = mChildren.getChildCount();
boolean found = false;
for (int i = count - 1; i >= 0; i--) {
final View child = mChildren.getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if ((child.getVisibility() == VISIBLE || child.getAnimation() != null)) {
child.getHitRect(frame);
if (getParent() instanceof HotSeat) { // 这里解决hotseat里长按图标无响应的问题
frame.offset(mOffsetX, getPaddingTop());
} else if (getParent() instanceof Workspace) { // 只有workspace里面的celllayout事件是基于DragLayer
frame.offset(getPaddingLeft(), getPaddingTop() + LauncherModel.getScreenOffset());
} else if (getParent() instanceof FolderLayout) {
frame.offset(getPaddingLeft(), getPaddingTop());
}
// 判断 framne 中是否有对应的坐标
if (frame.contains(x, y)) {
cellInfo.view = child;
cellInfo.cellX = lp.cellX;
cellInfo.cellY = lp.cellY;
cellInfo.cellHSpan = lp.cellHSpan;
cellInfo.cellVSpan = lp.cellVSpan;
//已经找到了,跳出循环
found = true;
break;
}
}
}
mLastDownOnOccupiedCell = found;
if (!found) {
final int cellXY[] = mTmpXY;
pointToCellExact(x, y, cellXY);
cellInfo.view = null;
cellInfo.cellX = cellXY[0];
cellInfo.cellY = cellXY[1];
cellInfo.cellHSpan = 1;
cellInfo.cellVSpan = 1;
}
setTag(cellInfo);
return found;
}
进行拖拽伴随着图标的震动和动画是怎样实现的?
刚开始以为在 DragView 的属性动画里实现,后面回过头才发现就在 WorkSpace 中调用 startChildShakeAnim(),实现桌面图标抖动。
public void startChildShakeAnim() {
for (int i = 0; i < getChildCount(); i++) {
if (getChildAt(i) instanceof CellLayout && getChildAt(i).getVisibility() != View.GONE) {
((CellLayout) getChildAt(i)).startChildDelAnim();
}
}
if (mLauncher.getHotSeat().getChildCount() > 0)
((CellLayout) mLauncher.getHotSeat().getChildAt(0)).startChildDelAnim();
mDraging = true;
setAllowVerticalScroll(false);
}