Launcher3分析之拖动图标的流程

来源:互联网 发布:淘宝网靠谱么 编辑:程序博客网 时间:2024/04/28 12:38

Launcher3分析之拖动图标的流程——按下

拖动图标是Launcher的特色,非常形象地解释了如何改变桌面的布局,让小白用户也可以轻松上手,这背后所做的工作也是把复杂的东西简单化,简单到按下-移动-松开。

今天说一说这个按下的过程是如何产生的。这里说的按是长按,长按就要找到他的长按监听器。在Workspace中addInScreen方法最后,给图标设置的监听器是Launcher对象,他实现了onLongClick方法。

拖动的流程-按下

onLongClick里面有一大坨东西,因为很多东西都需要处理长按事件。我们直接看函数最后的调用Workspace.startDrag,这里就是拖动的起点,函数名很直观“开始拖动”。

startdrag这里做一些准备工作,先把点击的图标设置成不可见,然后放弃焦点,将按下的状态设置成false,设置成false之后,按下时候的白边就消失了。接下来通过createDragOutline生成一个轮廓线,这个轮廓线就是用来指示你图标会被放在哪个位置的,你不断拖动,这个轮廓线的位置也在不断变化。最后进入beginDragShared方法,他其实就干了两个事,生成随手动的Bitmap,把工作交接给DragController.startDrag。

DragController是桌面里面的拖拽控制器,一旦他的startDrag执行完了,就可以接收滑动的触摸消息了。

startDrag-dragcontroller在上图中,开始调用了拖拽监听器(DragListener之后会讲),然后把mDragging设为true,标识他进入了拖拽过程。mDragObject包含了拖拽时候所需的必要信息,在其他很多地方都有调用。而dragView使随手而动的view,通过上面生成的那个bitmap合成而来,通过show方法显示在某个位置上,你在屏幕上滑动的时候,这个dragView的坐标也在一直变化。handleMoveEvent在最后会被调用,他是用来处理ACTION_MOVE事件的,你可能会问,这个时候还没有开始拖动,怎么会有MOVE事件呢?其实。。。。。我也不知道,哈哈

下一次会说说ACTION_MOVE的处理流程,包括DropTarget,DragSource等概念。

Launcher3分析之拖动图标的流程——移动


移动的逻辑还是比较清楚的,因为他抽象出来的模型十分易于理解的。

DropTarget是一个可放置(drop)区域的抽象,也就是我们松开手的时候想要把图标放到某个东西上,这个东西就是DropTarget,实现他的都是View,比如说文件夹,Workspace,删除区等等,你可以通过“ Open Type Hierarchy”来查看哪些类继承了DropTarget接口。下图是他比较重要的几个接口:

droptarget

  • onDragEnter是当拖动图标到某一DropTarget(蓝色),边缘,刚刚进入DropTarget范围内的时候所调用的内容。比如说我们拖动桌面的一个快捷方式,到桌面顶端的删除区域,“删除”两字和手中的图标会变红,这些动作都是在onDragEnter回调中完成的
  • onDragOver是在某一DropTarget内部移动的时候会调用的回调,比如我们把手上的图标移动到两个图标中间的时候,会发生挤位的情况(就是桌面已有图标让出空位),基本上每个ACTION_MOVE操作都会调用他。
  • onDragExit是从某一DropTarget拖出时候会进行的回调,比如onDragEnter时变红的“删除”和图标会在这个调用中恢复正常。
  • onDrop是松手时候发生的调用,做一些放下时候的操作,比如删除快捷方式的时候会在onDrop里面开始删除的操作。

 

拖动的工作原理说完了,代码就容易理解多了。

起点在DragLayer的onInterceptTouchEvent中,由于DragController接管了DragLayer的滑动事件处理,所以查看DragController的onInterceptTouchEvent方法,由于mDragging是true(返回上一篇回想下什么时候变成true的),所以跳到DragController的onTouchEvent方法,其中的ACTION_MOVE的处理内容基本都在handleMoveEvent方法中。

首先他把拖动的图标移动下位置,然后找到当前坐标下对应的DropTarget,再在checkTouchMove方法中完成上文提到的DropTarget中的一系列回调。最后,在checkScrollState查看是否滑到了屏幕边缘以便切换到其他屏。


Launcher3分析之拖动图标的流程——放下



我们来看下放下的过程,对应的触摸事件是ACTION_UP,我们直接跳到DragController的onTouchEvent。在处理ACTION_UP的时候多调用了一次handleMoveEvent,可能是因为ACTION_UP相对于上一次的时间也会有位置的变化。

up松手的时候会判断是是不是在“扔”图标(通过isFlingingToDelete),如果你用的不仔细的话,可能发现不了这个扔的过程:在说面拖动某个图标,然后快速甩向屏幕上方,就会触发删除的事件。这里检查你是否在扔是通过VelocityTracker来计算的,他会记录每次事件,然后计算出速度,包括X方向和Y方向的。如果不是在“扔”,就会调用放下图标最重要的一个方法,drop方法。

drop

首先他会通过当前的坐标找到需要放到的DropTarget,放下的操作首先得保证有DropTarget,然后DropTarget还得需要接受你手上的图标,比如你把图标拖到已经放满的Hotseat上去肯定不能被接受(DropTarget.acceptDrop返回false)。被接受了就可以调用当前DropTarget的onDrop方法了,我们以Workspace为例,他做了一下几件事:

  • 寻找最近的位置
  • 如果这个位置已经被占用,如果是快捷方式,会在条件满足的时候创建文件夹,结束
  • 如果这个位置已经被占用,如果是文件夹,会在满足条件的时候把图标放入文件夹,结束
  • 如果上面的条件都没有满足,找到一个空位,没找到的话,弹回原来的位置。
  • 如果找到空位,则会改变位置,修改数据库。
  • 生成一个往下放的动画,清除dragView。

上面的过程代码十分繁杂,因为没有对这方面进行过修改和自定义,所以也有很多不清楚的地方。

在drop方法里面,最后还有一个调用,这个调用的是DragSource的方法,DragSource代表着拖动的源头,跟DropTarget正好相反,DragSource的子类也就是Workspace,文件夹,widget列表。onDropCompleted是DragSource最重要的回调,主要做一些善后的操作。

drop()方法执行完毕之后,需要调用DragController.endDrag(),这个方法会把mDragging置为false,清理dragview,调用DragListener的onDragEnd方法。最后释放VelocityTracker,等待下一轮触摸事件的来临。





原创粉丝点击