zoukankan      html  css  js  c++  java
  • 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.   
    4.      // Make sure the drag was started by a long press as opposed to a long click.  
    5.      if (!child.isInTouchMode()) {  
    6.          return;  
    7.      }  
    8.   
    9.      mDragInfo = cellInfo;  
    10.      //隐藏拖拽的child  
    11.      child.setVisibility(GONE);  
    12.   
    13.      child.clearFocus();  
    14.      child.setPressed(false);  
    15.   
    16.      final Canvas canvas = new Canvas();  
    17.   
    18.      // We need to add extra padding to the bitmap to make room for the glow effect  
    19.      final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;  
    20.   
    21.      // The outline is used to visualize where the item will land if dropped  
    22.      mDragOutline = createDragOutline(child, canvas, bitmapPadding);  
    23.      beginDragShared(child, this);  
    24.  }  
     

    上面的代码主要做的工作是:把正在拖拽的这个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.   
    9.         mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),  
    10.                 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect);  
    11.         b.recycle();  
    12.     }  
     

    这个方法做的工作是:开始进行拖拽,绘制正在拖拽的图片,把拖拽的事件交给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.   
    11.         final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,  
    12.                 registrationY, 0, 0, b.getWidth(), b.getHeight());  
    13.   
    14.         if (dragOffset != null) {  
    15.             dragView.setDragVisualizeOffset(new Point(dragOffset));  
    16.         }  
    17.         if (dragRegion != null) {  
    18.             dragView.setDragRegion(new Rect(dragRegion));  
    19.         }  
    20.   
    21.         dragView.show(mMotionDownX, mMotionDownY);  
    22.         handleMoveEvent(mMotionDownX, mMotionDownY);  
    23.     }  
     

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

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

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

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

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

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

    这个类的作用是存储一些坐标,拖拽点距离整个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.   
    9.         final int action = ev.getAction();  
    10.         final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());  
    11.         final int dragLayerX = dragLayerPos[0];  
    12.         final int dragLayerY = dragLayerPos[1];  
    13.   
    14.         switch (action) {  
    15.         case MotionEvent.ACTION_DOWN:  
    16.             // Remember where the motion event started  
    17.             mMotionDownX = dragLayerX;  
    18.             mMotionDownY = dragLayerY;  
    19.   
    20.             if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {  
    21.                 mScrollState = SCROLL_WAITING_IN_ZONE;  
    22.                 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);  
    23.             } else {  
    24.                 mScrollState = SCROLL_OUTSIDE_ZONE;  
    25.             }  
    26.             break;  
    27.         case MotionEvent.ACTION_MOVE:  
    28.             handleMoveEvent(dragLayerX, dragLayerY);  
    29.             break;  
    30.         case MotionEvent.ACTION_UP:  
    31.             // Ensure that we've processed a move event at the current pointer location.  
    32.             handleMoveEvent(dragLayerX, dragLayerY);  
    33.   
    34.             mHandler.removeCallbacks(mScrollRunnable);  
    35.             if (mDragging) {  
    36.                 drop(dragLayerX, dragLayerY);  
    37.             }  
    38.             endDrag();  
    39.             break;  
    40.         case MotionEvent.ACTION_CANCEL:  
    41.             cancelDrag();  
    42.             break;  
    43.         }  
    44.   
    45.         return true;  
    46.     }  
     

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

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

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

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

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

  • 相关阅读:
    hdu 6836
    2019 树形—DP
    2020牛客暑期多校训练营(第六场)
    hdu 6756 Finding a MEX 线段树
    2020 Multi-University Training Contest 2
    spring boot maven 打jar包 不能引入外部jar
    git 操作命令
    homestead 安装swoole
    MYSQL-触发器
    再次认知const
  • 原文地址:https://www.cnblogs.com/xiaochao1234/p/4053038.html
Copyright © 2011-2022 走看看