当前位置: 代码迷 >> Android >> Android Launcher拖拽事件详解【android4.0-Launcher系列2】
  详细解决方案

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

热度:97   发布时间:2016-05-01 10:52:53.0
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()方法中的一部分代码:

<strong> </strong><span style="color:#000000;FONT-SIZE: 16px">// Setup the workspace        mWorkspace.setHapticFeedbackEnabled(false);        mWorkspace.setOnLongClickListener(this);        mWorkspace.setup(dragController);        dragController.addDragListener(mWorkspace);</span>
?

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

?

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

public boolean onLongClick(View v) {         ·············· // The hotseat touch handling does not go through Workspace, and we always allow long press        // on hotseat items.        final View itemUnderLongClick = longClickCellInfo.cell;        boolean allowLongPress = isHotseatLayout(v) || mWorkspace.allowLongPress();        if (allowLongPress && !mDragController.isDragging()) {            if (itemUnderLongClick == null) {                // User long pressed on empty space                mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);                startWallpaper();            } else {                if (!(itemUnderLongClick instanceof Folder)) {                    // User long pressed on an item                    mWorkspace.startDrag(longClickCellInfo);                }            }        }        return true;    }
?

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

?

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

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

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

?

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

 public void beginDragShared(View child, DragSource source) {    ··· ···// Clear the pressed state if necessary        if (child instanceof BubbleTextView) {            BubbleTextView icon = (BubbleTextView) child;            icon.clearPressedOrFocusedBackground();        }        mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),                DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect);        b.recycle();    }
?

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

?

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

 public void startDrag(Bitmap b, int dragLayerX, int dragLayerY,            DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion) {··· ··· mDragObject.dragComplete = false;        mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);        mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);        mDragObject.dragSource = source;        mDragObject.dragInfo = dragInfo;mVibrator.vibrate(VIBRATE_DURATION);        final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,                registrationY, 0, 0, b.getWidth(), b.getHeight());        if (dragOffset != null) {            dragView.setDragVisualizeOffset(new Point(dragOffset));        }        if (dragRegion != null) {            dragView.setDragRegion(new Rect(dragRegion));        }        dragView.show(mMotionDownX, mMotionDownY);        handleMoveEvent(mMotionDownX, mMotionDownY);    }
?

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

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

boolean isDropEnabled();void onDrop(DragObject dragObject);    void onDragEnter(DragObject dragObject);    void onDragOver(DragObject dragObject);    void onDragExit(DragObject dragObject);DropTarget getDropTargetDelegate(DragObject dragObject);boolean acceptDrop(DragObject dragObject);    // These methods are implemented in Views    void getHitRect(Rect outRect);    void getLocationInDragLayer(int[] loc);    int getLeft();    int getTop();
?

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

?

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

class DragObject {        public int x = -1;        public int y = -1;        /** X offset from the upper-left corner of the cell to where we touched.  */        public int xOffset = -1;        /** Y offset from the upper-left corner of the cell to where we touched.  */        public int yOffset = -1;        /** This indicates whether a drag is in final stages, either drop or cancel. It         * differentiates onDragExit, since this is called when the drag is ending, above         * the current drag target, or when the drag moves off the current drag object.         */        public boolean dragComplete = false;        /** The view that moves around while you drag.  */        public DragView dragView = null;        /** The data associated with the object being dragged */        public Object dragInfo = null;        /** Where the drag originated */        public DragSource dragSource = null;        /** Post drag animation runnable */        public Runnable postAnimationRunnable = null;        /** Indicates that the drag operation was cancelled */        public boolean cancelled = false;        public DragObject() {        }    }
?

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

?

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

/**     * Call this from a drag source view.     */    public boolean onTouchEvent(MotionEvent ev) {        if (!mDragging) {            return false;        }        final int action = ev.getAction();        final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());        final int dragLayerX = dragLayerPos[0];        final int dragLayerY = dragLayerPos[1];        switch (action) {        case MotionEvent.ACTION_DOWN:            // Remember where the motion event started            mMotionDownX = dragLayerX;            mMotionDownY = dragLayerY;            if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {                mScrollState = SCROLL_WAITING_IN_ZONE;                mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);            } else {                mScrollState = SCROLL_OUTSIDE_ZONE;            }            break;        case MotionEvent.ACTION_MOVE:            handleMoveEvent(dragLayerX, dragLayerY);            break;        case MotionEvent.ACTION_UP:            // Ensure that we've processed a move event at the current pointer location.            handleMoveEvent(dragLayerX, dragLayerY);            mHandler.removeCallbacks(mScrollRunnable);            if (mDragging) {                drop(dragLayerX, dragLayerY);            }            endDrag();            break;        case MotionEvent.ACTION_CANCEL:            cancelDrag();            break;        }        return true;    }
?

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

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

private void handleMoveEvent(int x, int y) {        mDragObject.dragView.move(x, y);        // Drop on someone?        final int[] coordinates = mCoordinatesTemp;        DropTarget dropTarget = findDropTarget(x, y, coordinates);        mDragObject.x = coordinates[0];        mDragObject.y = coordinates[1];        if (dropTarget != null) {            DropTarget delegate = dropTarget.getDropTargetDelegate(mDragObject);            if (delegate != null) {                dropTarget = delegate;            }            if (mLastDropTarget != dropTarget) {                if (mLastDropTarget != null) {                    mLastDropTarget.onDragExit(mDragObject);                }                dropTarget.onDragEnter(mDragObject);            }            dropTarget.onDragOver(mDragObject);        } else {            if (mLastDropTarget != null) {                mLastDropTarget.onDragExit(mDragObject);            }        }        mLastDropTarget = dropTarget;··· ···}
???

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

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

本文转载自:http://blog.csdn.net/wdaming1986/article/details/7671318

  相关解决方案