Android4.4-Launcher源码分析系列之关键的类和接口之DragScroller、DragController

原创 2016年06月01日 10:37:25

一、DragScroller

DragScroller是一个接口,顾名思义是控制滑动的接口,它定义了4个方法

public interface DragScroller {
  	/**
	 * 左滑
	 */
	void scrollLeft();
    
	/**
	 * 右滑
	 */
    void scrollRight();

    /**
     *进入滑动区域
     */
    boolean onEnterScrollArea(int x, int y, int direction);

    /**
     *退出滑动区域
     */
    boolean onExitScrollArea();
}
想具体分析它还是要结合WorkSpace和DragController,

二、DragController

DragController是一个类,不继承任何父类,内部提供了一个接口DragListener

interface DragListener {

        void onDragStart(DragSource source, Object info, int dragAction);

        void onDragEnd();
    }

onDragStart————拖动图标开始时执行的方法

onDragEnd———— 拖动图标结束时执行的方法

DragController是处理拖动图标的类,那么我们自然得关注它的触摸事件.拖动图标的流程是这样的


Launcher和WorkSpace后面会详解,大家目前只要知道这个流程即可.Launcher的onLongClick事件会调用WorkSpace的startDrag方法,startDrag方法会调用beginDragShared方法, beginDragShared方法最终调用DragController的startDrag方法.好,我们先看下这个startDrag方法.

 /**
 	 * @param b            拖动对象的图像,有可能会被缩放
	 * @param dragLayerX   拖动对象的图像x坐标
	 * @param dragLayerY   拖动对象的图像y坐标
	 * @param source       发起拖动的对象
	 * @param dragInfo     被拖动对象的数据
	 * @param dragAction   拖放的动作:移动或者复制
	 * @param dragRegion   拖动对象bitmap的区域  
	 */
      public void startDrag(Bitmap b, int dragLayerX, int dragLayerY,
            DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion,
            float initialDragViewScale) {
    	
    	  if (PROFILE_DRAWING_DURING_DRAG) {
            android.os.Debug.startMethodTracing("Launcher");
        }
         // 隐藏软键盘
    	  if(mInputMethodManager == null) {
            mInputMethodManager = (InputMethodManager)
                    mLauncher.getSystemService(Context.INPUT_METHOD_SERVICE);
         }
        mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);

        for (DragListener listener : mListeners) {
            listener.onDragStart(source, dragInfo, dragAction);
        }
        //记住手指点击位置与屏幕左上角位置偏差
        final int registrationX = mMotionDownX - dragLayerX;
        final int registrationY = mMotionDownY - dragLayerY;

        final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
        final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;

        mDragging = true;
        //创建DragObject对象
        mDragObject = new DropTarget.DragObject();

        mDragObject.dragComplete = false;
        mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
        mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
        mDragObject.dragSource = source;
        mDragObject.dragInfo = dragInfo;          
        //创建DragView对象
        final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
                registrationY, 0, 0, b.getWidth(), b.getHeight(), initialDragViewScale);

         
        if (dragOffset != null) {
            dragView.setDragVisualizeOffset(new Point(dragOffset));
        }
        if (dragRegion != null) {
            dragView.setDragRegion(new Rect(dragRegion));
        }

        mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        //显示Dragview对象
        dragView.show(mMotionDownX, mMotionDownY);
        handleMoveEvent(mMotionDownX, mMotionDownY);
    }

事实上还有一个startDrag的重载方法,不过我不知道它是什么时候执行的,这个重载方法最终还是进入到图中的startDrag方法.
根据传入的一些参数创建了一个DragView,DragView就是我们拖动图标的时候显示的图片,为了验证一下,在这里我做一个修改

Bitmap mbBitmap=BitmapFactory.decodeResource(mLauncher.getResources(), R.drawable.hand);
        //创建DragView对象
        final DragView dragView = mDragObject.dragView = new DragView(mLauncher, mbBitmap, registrationX,
                registrationY, 0, 0, b.getWidth(), b.getHeight(), initialDragViewScale);
我传入了一张手指的Bitmap,其他参数不变,我们看下效果.



最后一行代码调用了handleMoveEventd方法,它是处理移动图标的方法

private void handleMoveEvent(int x, int y) {
        //跟随手指移动dragview
    	mDragObject.dragView.move(x, y);
        
        final int[] coordinates = mCoordinatesTemp;
        // 根据手指所在屏幕坐标获取目前所在的拖放view的容器
        DropTarget dropTarget = findDropTarget(x, y, coordinates);
        mDragObject.x = coordinates[0];
        mDragObject.y = coordinates[1];
        checkTouchMove(dropTarget);
       
        // Check if we are hovering over the scroll areas
        mDistanceSinceScroll +=Math.sqrt(Math.pow(mLastTouch[0] - x, 2) + Math.pow(mLastTouch[1] - y, 2));
        mLastTouch[0] = x;
        mLastTouch[1] = y;
        checkScrollState(x, y);
    }
根据传入的坐标会调用mDragObject.dragView.move方法移动dragview,然后获取Dragview当前处于的容器DropTarget,然后调用checkTouchMove方法

 private void checkTouchMove(DropTarget dropTarget) {
     	if (dropTarget != null) {
        	// 如果还没有设置dropTarget或者dropTarget已经发生了改变,则设置dropTarget,调用onDragEnter
        	if (mLastDropTarget != dropTarget) {
                if (mLastDropTarget != null) {
                    //退出这个DropTarget
                	mLastDropTarget.onDragExit(mDragObject);
                 }
                dropTarget.onDragEnter(mDragObject);
                	
        	}
            dropTarget.onDragOver(mDragObject);
        } else {
        	// 如果没有dropTarget并且上次处理的mLastDropTarget不为空则mLastDropTarget调用移出事件
        	if (mLastDropTarget != null) {
                mLastDropTarget.onDragExit(mDragObject);
            }
        }
        mLastDropTarget = dropTarget;
    }
如果传入的容器DropTarget不为空,那么如果当前的容器DropTarget与上一次的DropTarget不同并且上一次的DropTarget不为空,则退出上一次的DropTarget,进入当前的DropTarget.

如果传入的容器DropTarget为空,上一次的DropTarget不为空,那么上一次的DropTarget移除当前DragObject

最后把mLastDropTarge重新t赋值为当前的dropTarget.

好了,checkTouchMove方法分析完了,回到handleMoveEventd方法往下看,会继续调用checkScrollState方法.

private void checkScrollState(int x, int y) {
     
    	final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop();
        final int delay = mDistanceSinceScroll < slop ? RESCROLL_DELAY : SCROLL_DELAY;
        final DragLayer dragLayer = mLauncher.getDragLayer();
        //是否从右到左
        final boolean isRtl = (dragLayer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
        final int forwardDirection = isRtl ? SCROLL_RIGHT : SCROLL_LEFT;
        final int backwardsDirection = isRtl ? SCROLL_LEFT : SCROLL_RIGHT;
        //如果从左往右拖动到滚动区域
        if (x < mScrollZone) {
        	// 进入可滚动区域后仍然要等待一会儿以后再滚动。 	
        	if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                mScrollState = SCROLL_WAITING_IN_ZONE;
                if (mDragScroller.onEnterScrollArea(x, y, forwardDirection)) {
                    Toast.makeText(mLauncher, "向左边滑动", 0).show();
                	dragLayer.onEnterScrollArea(forwardDirection);
                    mScrollRunnable.setDirection(forwardDirection);
                    mHandler.postDelayed(mScrollRunnable, delay);
                }
            }
        } 
        //如果从右往左拖动到滚动区域
        else if (x > mScrollView.getWidth() - mScrollZone) {
        	System.out.println("...................区域外");
        	if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                mScrollState = SCROLL_WAITING_IN_ZONE;
                if (mDragScroller.onEnterScrollArea(x, y, backwardsDirection)) {
                	Toast.makeText(mLauncher, "向右边滑动", 0).show();
                	dragLayer.onEnterScrollArea(backwardsDirection);
                    mScrollRunnable.setDirection(backwardsDirection);
                    mHandler.postDelayed(mScrollRunnable, delay);
                }
            }
        } 
        // 在滚动之前进入了不滚动区域,则取消滚动
        else {
            clearScrollRunnable();
        }
    }
ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop()是获取可以触发移动事件的最短距离,赋值给slop.
mDistanceSinceScroll是上一次手指移动的距离,如果mDistanceSinceScroll小于slop,那么delay取值RESCROLL_DELAY,默认是900,否则取SCROLL_DELAY,默认值为500.

isRtl是判断是否从右到左移动,is Right To Left的意思.然后根据isRtl的值取forwardDirection和backwardsDirection,这两个是往前和往后的标志.

如果x坐标小于mScrollZone,说明是从左到右拖动图标,那么进行滑动到右边屏幕的操作

如果x坐标大于当前屏幕宽度和可滑动区域的差值,说明是从右到左拖动图标,那么进行滑动到左边屏幕的操作

否则取消滚动.

checkTouchMove方法执行完后,会执行onTouchEvent方法,我觉得奇怪的是onTouchEvent的down事件无法触发,不知道什么原因.

move事件里又调用了handleMoveEvent方法.

up事件里就停止拖动,做一些恢复操作.

cancel事件里清除handler消息队列,取消拖动

public boolean onTouchEvent(MotionEvent ev) {
        if (!mDragging) {
            return false;
        }
        // Update the velocity tracker
        acquireVelocityTrackerAndAddMovement(ev);
    
        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;
            }
            handleMoveEvent(dragLayerX, dragLayerY);
            break;
        case MotionEvent.ACTION_MOVE:
        	handleMoveEvent(dragLayerX, dragLayerY);
            break;
        case MotionEvent.ACTION_UP:
        	 
        	// 确保我们在抬起点处理了事件
            handleMoveEvent(dragLayerX, dragLayerY);
            mHandler.removeCallbacks(mScrollRunnable);
            if (mDragging) {
                PointF vec = isFlingingToDelete(mDragObject.dragSource);
                if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragInfo)) {
                    vec = null;
                }
                if (vec != null) {
                    dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
                } else {
                    drop(dragLayerX, dragLayerY);
                }
            }
            endDrag();
            break;
        case MotionEvent.ACTION_CANCEL:
            mHandler.removeCallbacks(mScrollRunnable);
            cancelDrag();
            break;
        }

        return true;
    }
DragController的分析就到此为止了.



 

 

 


版权声明:本文为博主原创文章,转载请注明出处:邓志勇博客 http://blog.csdn.net/qq_31530015/

相关文章推荐

Android4.4-Launcher源码分析系列之关键的类和接口之DropTarget、DragSource

一、DropTarget DropTarget是一个接口,它定义了一个可以接收被拖动对象的对象,意思是说实现了这个接口的对象是一个可以放置被拖动对象的容器. 实现了这个接口的有ButtonDr...

Android4.4-Launcher源码分析系列之关键的类和接口之DragLayer

一、DragLayer布局 上一篇文章分析过Launcher的布局,它是最外层的布局

Android4.4-Launcher源码分析系列之CellLayout

一.CellLayout是什么 在前面的 Android4.4-Launcher源码分析系列之Launcher介绍分析了Launcher的布局,CellLayout继承自ViewGroup, 一个Wo...

Android4.4-Launcher源码分析系列之概述

这段时间研究了安卓4.4的Launcher源码,决定把自己的收获分享一下,欢迎各位留言. 把4.4的Launcher源码导入到eclipse中时会报一些错误,这是因为涉及到一些隐藏的api和资源.需要...

Android4.4-Launcher源码分析系列之搜索框/删除框

一、搜索框/删除框简介 搜索框在手机桌面上方,当拖动一个快捷方式图标时就会隐藏搜索框并且显示删除框.如下图所示 二、搜索框/删除框布局   屏幕上方的搜索和删除框是在一个布局里,名称为qsb...

Android4.4-Launcher源码分析系列之WorkSpace及屏幕滑动

一.WorkSpace是什么 前面已经介绍了一个WorkSpace包含了多个CellLayout,再回忆下之前画过的图 WorkSpace是一个ViewGroup,它的布局如下 ...

Android4.4-Launcher源码分析系列之Launcher介绍

一.Launcher是什么 Launcher是启动器的意思,最直观的就是手机上的桌面.其实它是个Activity. public class Launcher extends Activity...

Android4.4-Launcher源码分析系列之Launcher界面修改、壁纸替换、图标替换、修改滚动指示器

一、Launcher加载布局的基本流程 Launcher界面的内容展现是由default_workspace.xml决定的,它决定了每个屏幕显示的内容和布局.位于res\xml目录下.它是在Laun...

Android4.4-Launcher源码分析系列之Launcher启动简介

一、Launcher的启动 首先启动LauncherApplication,这里面只有两行代码,初始化了LauncherAppState,LauncherAppState主要是初始化一些对象,注册广播...

Android 4.0 Launcher源码分析系列

桌面的左右滑动功能主要是在PagedView类中实现的,而WorkSpace是PagedView类的子类,所以会继承PagedView中的方法。当我们的手指点击WorkSpace时,首先就会触发Pag...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android4.4-Launcher源码分析系列之关键的类和接口之DragScroller、DragController
举报原因:
原因补充:

(最多只允许输入30个字)