Android Launcher拖拽事件详解【android4.0--Launcher系列二】

AndroidICS4.0版本的launcher拖 拽的流程,基本和2.3的相似。就是比2.3写的封装的接口多了一些,比如删除类的写法就多了个类。等等。4.0的改变有一些,但是不是特别大。这个月一 直在改动Launcher的缩略图的效果,4.0的缩略图的功能没有实现,还得从2.3的Launcher中摘出来。通过做这个缩略图对Launcher 的模块有一点点了解,拿来分享一下Launcher拖拽的工作流程。有图有真相!

  (1) 先来看看类之间的继承关系

     图(1)

 (2)再来看看Launcher拖拽流程的时序图

                                                                       图(2)

 

下面咱们分步来解析Launcher拖拽的详细过程:

step 1 :先来看看Launcher.java这个类的onCreate()方法中的setupViews()方法中的一部分代码:

Java代码  

  1. // Setup the workspace
  2. mWorkspace.setHapticFeedbackEnabled(false);
  3. mWorkspace.setOnLongClickListener(this);
  4. mWorkspace.setup(dragController);
  5. dragController.addDragListener(mWorkspace);

 

Workspace设置长按事件的监听交给了Launcher.java这个类了。所以在主屏上长按事件会走到Launcher.java----->onLongClick()这个方法中去;

step 2 :接着我们来看看Launcher.java中onLongClick()的代码:

Java代码  

  1. public boolean onLongClick(View v) {
  2. ··············
  3. // The hotseat touch handling does not go through Workspace, and we always allow long press
  4. // on hotseat items.
  5. final View itemUnderLongClick = longClickCellInfo.cell;
  6. boolean allowLongPress = isHotseatLayout(v) || mWorkspace.allowLongPress();
  7. if (allowLongPress && !mDragController.isDragging()) {
  8. if (itemUnderLongClick == null) {
  9. // User long pressed on empty space
  10. mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
  11. HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
  12. startWallpaper();
  13. } else {
  14. if (!(itemUnderLongClick instanceof Folder)) {
  15. // User long pressed on an item
  16. mWorkspace.startDrag(longClickCellInfo);
  17. }
  18. }
  19. }
  20. return true;
  21. }

通过itemUnderLongClick == null 来判断,在屏幕上触发长按事件是否选中了shortcut或者widget。如果为空,就启动桌面的壁纸,else,就把拖拽事件往Workspace.java这个类传递。

Step 3 :通过mWorkspace.startDrag(longClickCellInfo),把长按事件传递给workspace来处理,具体来看代码:

Java代码  

  1. void startDrag(CellLayout.CellInfo cellInfo) {
  2. View child = cellInfo.cell;
  3. // Make sure the drag was started by a long press as opposed to a long click.
  4. if (!child.isInTouchMode()) {
  5. return;
  6. }
  7. mDragInfo = cellInfo;
  8. //隐藏拖拽的child
  9. child.setVisibility(GONE);
  10. child.clearFocus();
  11. child.setPressed(false);
  12. final Canvas canvas = new Canvas();
  13. // We need to add extra padding to the bitmap to make room for the glow effect
  14. final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
  15. // The outline is used to visualize where the item will land if dropped
  16. mDragOutline = createDragOutline(child, canvas, bitmapPadding);
  17. beginDragShared(child, this);
  18. }

 

上面的代码主要做的工作是:把正在拖拽的这个view隐藏掉,在主屏幕上绘制一个蓝色的,大小和图标相似的一个边框,以表示能在主屏的这个位置放置。

Step 4 :接着调用beginDragShared(child, this)这个方法,代码如下:

Java代码  

  1. public void beginDragShared(View child, DragSource source) {
  2. ··· ···
  3. // Clear the pressed state if necessary
  4. if (child instanceof BubbleTextView) {
  5. BubbleTextView icon = (BubbleTextView) child;
  6. icon.clearPressedOrFocusedBackground();
  7. }
  8. mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
  9. DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect);
  10. b.recycle();
  11. }

这个方法做的工作是:开始进行拖拽,绘制正在拖拽的图片,把拖拽的事件交给DragController来处理。

Step 5 :接 着来看看mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect)这个方法,代码如下:

Java代码  

  1. public void startDrag(Bitmap b, int dragLayerX, int dragLayerY,
  2. DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion) {
  3. ··· ···
  4. mDragObject.dragComplete = false;
  5. mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
  6. mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
  7. mDragObject.dragSource = source;
  8. mDragObject.dragInfo = dragInfo;
  9. mVibrator.vibrate(VIBRATE_DURATION);
  10. final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
  11. registrationY, 0, 0, b.getWidth(), b.getHeight());
  12. if (dragOffset != null) {
  13. dragView.setDragVisualizeOffset(new Point(dragOffset));
  14. }
  15. if (dragRegion != null) {
  16. dragView.setDragRegion(new Rect(dragRegion));
  17. }
  18. dragView.show(mMotionDownX, mMotionDownY);
  19. handleMoveEvent(mMotionDownX, mMotionDownY);
  20. }

 

这 个方法的作用是:计算要拖拽的view的大小,显示在workspace上,dragView.show(mMotionDownX, mMotionDownY);这个show()会根据手指的移动而移动的。然后在通过handleMoveEvent()方法来分发拖拽的目标到底在哪个 目标上。DropTarget一共有3个:workspace,ButtonDropTarget(删除类),Folder;他们分别实现了 DropTarget这个接口。

下面来看看这个接口有一下几个方法:

Java代码  

  1. boolean isDropEnabled();
  2. void onDrop(DragObject dragObject);
  3. void onDragEnter(DragObject dragObject);
  4. void onDragOver(DragObject dragObject);
  5. void onDragExit(DragObject dragObject);
  6. DropTarget getDropTargetDelegate(DragObject dragObject);
  7. boolean acceptDrop(DragObject dragObject);
  8. // These methods are implemented in Views
  9. void getHitRect(Rect outRect);
  10. void getLocationInDragLayer(int[] loc);
  11. int getLeft();
  12. int getTop();

 

这些方法不是每个类继承了DropTarget的接口,都要把每个方法都实现,这要看具体的需要来定。

另外这个接口中有个内部类-----DragObject:如下

Java代码  

  1. class DragObject {
  2. public int x = -1;
  3. public int y = -1;
  4. /** X offset from the upper-left corner of the cell to where we touched.  */
  5. public int xOffset = -1;
  6. /** Y offset from the upper-left corner of the cell to where we touched.  */
  7. public int yOffset = -1;
  8. /** This indicates whether a drag is in final stages, either drop or cancel. It
  9. * differentiates onDragExit, since this is called when the drag is ending, above
  10. * the current drag target, or when the drag moves off the current drag object.
  11. */
  12. public boolean dragComplete = false;
  13. /** The view that moves around while you drag.  */
  14. public DragView dragView = null;
  15. /** The data associated with the object being dragged */
  16. public Object dragInfo = null;
  17. /** Where the drag originated */
  18. public DragSource dragSource = null;
  19. /** Post drag animation runnable */
  20. public Runnable postAnimationRunnable = null;
  21. /** Indicates that the drag operation was cancelled */
  22. public boolean cancelled = false;
  23. public DragObject() {
  24. }
  25. }

这个类的作用是存储一些坐标,拖拽点距离整个view左上角x轴上的距离,y轴上的距离,还有一些拖拽的信息都保存在这个类中,还有动画线程类等等。在拖拽过程中这些信息都是会用到的。

Step 6 :接 着来看看handleMoveEvent()这个类,这个类频繁被调用,因为在DragLayer.java这个类中onTouchEvent()方法, 最后调用的是 mDragController.onTouchEvent(ev)这个方法,长按后,移动的事件就传递到了DragController中的 onTouchEvent()方法中,先来看看mDragController.onTouchEvent(ev)这个方法,代码如下:

Java代码  

  1. /**
  2. * Call this from a drag source view.
  3. */
  4. public boolean onTouchEvent(MotionEvent ev) {
  5. if (!mDragging) {
  6. return false;
  7. }
  8. final int action = ev.getAction();
  9. final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
  10. final int dragLayerX = dragLayerPos[0];
  11. final int dragLayerY = dragLayerPos[1];
  12. switch (action) {
  13. case MotionEvent.ACTION_DOWN:
  14. // Remember where the motion event started
  15. mMotionDownX = dragLayerX;
  16. mMotionDownY = dragLayerY;
  17. if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
  18. mScrollState = SCROLL_WAITING_IN_ZONE;
  19. mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
  20. } else {
  21. mScrollState = SCROLL_OUTSIDE_ZONE;
  22. }
  23. break;
  24. case MotionEvent.ACTION_MOVE:
  25. handleMoveEvent(dragLayerX, dragLayerY);
  26. break;
  27. case MotionEvent.ACTION_UP:
  28. // Ensure that we‘ve processed a move event at the current pointer location.
  29. handleMoveEvent(dragLayerX, dragLayerY);
  30. mHandler.removeCallbacks(mScrollRunnable);
  31. if (mDragging) {
  32. drop(dragLayerX, dragLayerY);
  33. }
  34. endDrag();
  35. break;
  36. case MotionEvent.ACTION_CANCEL:
  37. cancelDrag();
  38. break;
  39. }
  40. return true;
  41. }

 

在这个方法中清楚的可以看见handleMoveEvent()这个方法会在move,up的时候频繁地调用。

现在再来看看这个handleMoveEvent()方法,看看它的庐山真面目:

Java代码  

  1. private void handleMoveEvent(int x, int y) {
  2. mDragObject.dragView.move(x, y);
  3. // Drop on someone?
  4. final int[] coordinates = mCoordinatesTemp;
  5. DropTarget dropTarget = findDropTarget(x, y, coordinates);
  6. mDragObject.x = coordinates[0];
  7. mDragObject.y = coordinates[1];
  8. if (dropTarget != null) {
  9. DropTarget delegate = dropTarget.getDropTargetDelegate(mDragObject);
  10. if (delegate != null) {
  11. dropTarget = delegate;
  12. }
  13. if (mLastDropTarget != dropTarget) {
  14. if (mLastDropTarget != null) {
  15. mLastDropTarget.onDragExit(mDragObject);
  16. }
  17. dropTarget.onDragEnter(mDragObject);
  18. }
  19. dropTarget.onDragOver(mDragObject);
  20. } else {
  21. if (mLastDropTarget != null) {
  22. mLastDropTarget.onDragExit(mDragObject);
  23. }
  24. }
  25. mLastDropTarget = dropTarget;
  26. ··· ···
  27. }

这 个方法的作用:通过findDropTarget(x, y, coordinates),来判断在哪个拖拽目标里面,然后通过下面的if判断来执行不同的onDragOver,onDragExit等的方法。这样就 在相应的类中去做处理,以后的事情就明朗了。这就是Launcher的拖拽事件的分发与处理,用到了MVC的思想,代码阅读起来还是比较顺利的。有图有真 相。

欢迎大家留言讨论相关问题。

时间: 2024-10-21 16:58:56

Android Launcher拖拽事件详解【android4.0--Launcher系列二】的相关文章

android事件详解

http://blog.csdn.net/asce1885/article/details/7596669 http://blog.csdn.net/liranke/article/details/6855601 http://blog.csdn.net/luoshengyang/article/details/8223770 http://www.cnblogs.com/mybkn/archive/2012/07/14/2590743.html http://blog.csdn.net/leo

Android APK优化工具Zipalign详解

最近在googl play上发布apk要优化 Android SDK中包含一个"zipalign"的工具,它能够对打包的应用程序进行优化.在你的应用程序上运行zipalign,使得在运行时Android与应用程序间的交互更加有效率.因此,这种方式能够让应用程序和整个系统运行得更快.我们强烈推荐在新的和已经发布的程序上使用zipalign工具来得到优化后的版本 一.这里下载android SDK,只为了用他的zipalign工具,当然什么时候大家有兴趣了用来开发两个小程序也是很简单的 A

Android开发之通知栏Notification详解

Notification的用法  --- 状态栏通知 发送一个状态栏通知必须的两个类: 1. NotificationManager   --- 状态栏通知的管理类,负责发通知,清除通知等 NotificationManager : 是一个系统Service,必须通过 context.getSystemService(NOTIFICATION_SERVICE)方法获取 NotificationManager notificationManager = (NotificationManager)

[Android新手区] SQLite 操作详解--SQL语法

该文章完全摘自转自:北大青鸟[Android新手区] SQLite 操作详解--SQL语法  :http://home.bdqn.cn/thread-49363-1-1.html SQLite库可以解析大部分标准SQL语言.但它也省去了一些特性并且加入了一些自己的新特性.这篇文档就是试图描述那些SQLite支持/不支持的SQL语法的.查看关键字列表. 如下语法表格中,纯文本用蓝色粗体显示.非终极符号为斜体红色.作为语法一部分的运算符用黑色Roman字体表示. 这篇文档只是对SQLite实现的SQ

Android Design Support Library使用详解

Android Design Support Library使用详解 Google在2015的IO大会上,给我们带来了更加详细的Material Design设计规范,同时,也给我们带来了全新的Android Design Support Library,在这个support库里面,Google给我们提供了更加规范的MD设计风格的控件.最重要的是,Android Design Support Library的兼容性更广,直接可以向下兼容到Android 2.2.这不得不说是一个良心之作. 使用S

使用 jQuery Mobile 与 HTML5 开发 Web App —— jQuery Mobile 事件详解

在前文<使用 jQuery Mobile 与 HTML5 开发 Web App —— jQuery Mobile 默认配置与事件基础>中,Kayo 对 jQuery Mobile 事件的基础作出了一些说明,建议在阅读本文前首先阅读前文,这里 Kayo 再引用前文的重要内容. “jQuery Mobile 在基于本地事件上,创建了一系列的自定义事件,大部分事件是基于触摸设备的使用情况开发的,当然这些事件对于桌面环境也会有适当的处理,开发者可以使用 bind() 函数绑定到需要的页面对象中. 值得

Android Gradle manifestPlaceholders 占位符详解

Android Gradle manifestPlaceholders 占位符详解 在实际项目中,AndroidManifest里十几个地方的值是需要动态的改变(生成apk文件的时候).如果每次去改也可以,但是累啊,在我之前他们打包是用手动替换,但我觉得这是是在没办法的办法,但是有了manifestPlaceholders占位符后就简单的多了,只需要改一个地方就行了. 1. 概括 下面介绍下manifestPlaceholders占位符的使用,其实很好理解,你可以认为它可以在 build.gra

Android开发之MediaRecorder类详解

MediaRecorder类详解 手机一般都有麦克风和摄像头,而Android系统就可以利用这些硬件来录制音视频了. 为了增加对录制音视频的支持,Android系统提供了一个MediaRecorder的类.该类的使用也非常简单,下面让我们来了解一下这个类: 一.类结构: java.lang.Object    ? android.media.MediaRecorder 二.类概述: 用于录制音频和视频的一个类. 三.状态图: 说明: 与MediaPlayer类非常相似MediaRecorder也

Android task和back stack详解(官方文档翻译)

一个应用往往包含很多activities.每个activity都应围绕着用户可执行的特定动作来设计,并且可以启动其它activitie.例如,一个email应用可能可能有一个显示新邮件列表的activity.当用户选择一个邮件,一个新的activity被打开以显示邮件内容. 一个activity也可以打开同一设备上存在于其它应用的activitie,例如,如果你的应用想要发送一个邮件,你可以定义一个intent来执行一个"send"动作并包含一些数据,比如一个地址和一条信息.另一个应用