当前位置:操作系统 > 安卓/Android >>

Android 4.0 Launcher2源码分析——Workspace滑动

Launcher桌面的一大功能就是支持左右滑动,这样的功能在现在的应用中使用非常广泛,并且有很多实现的方式,可以通过使用Fragment来实现也可以通过自定义的控件来实现。
Launcher采用了后者,这一功能的实现在Workspace来完成。首先来看一下Workspace的继承关系:

从图中可以看出Workspace是PagedView的子类,而实际上滑动功能的实现是在PagedView中实现的。在Launcher中,Workspace中有五个CellLayout,分别代表五个分屏。
当左右拖动CellLayout时,就能实现滑动的效果。但实际上CellLayout中还有其他的子View,PagedView是如何避免了来自子View的干扰的呢?这里就需要讨论另一个问题,
Android对touch事件的拦截制度。而拦截发生在ViewGroup的onInterceptTouchEvent()和onTouchEvent()以及View的onTouchEvent中。当发生touch事件时,系统会产生一个
MotionEvent并且沿着View Tree开始传递。首先获取MotionEvent是View Tree的根节点,根节点通常是一个ViewGroup,ViewGroup将在onInterceptTouchEvent()中获取
MotionEvent并决定是否继续向下传递。当在ViewGroup.onInterceptEvent()中返回true时,将截获MotionEvent,View Tree下面的View将无法获得MotionEvent,
转而交给当前ViewGroup的onTouchEvent()方法。如果onTouchEvent中返回false,那么MotionEvent将沿着View Tree向上传给上一层。拦截的过程草图如下:

有了touch事件的拦截机制之后,View tree中的各个层之间的分工也就更加明确了。在Launcher的View tree中,从上到下的主要的节点有,DragLayer,Workspace,
CellLayout。DragLayer层的主要任务是负责对图标和AppWidget进行拖拽,Workspace则主要负责左右滑动,CellLayout则用于容纳各种桌面的快捷方式。大概的分工如下:

那么现在就进入PagedView中去了解实现的过程吧。
滑动功能主要分两步:1、在onInterceptTouchEvent中进行拦截。2、在onTouchEvent中进行滑动。
1,onInterceptTouchEvent(MotionEvent en)
在这个方法中,决定了什么时候截获MotionEvent来实现滑动,避免了子View的其他事件的影响(如点击事件)。
[java] 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
     /** 
     * This method JUST determines whether we want to intercept the motion.
     * If we return true, onTouchEvent will be called and we do the actual
     * scrolling there.
     **/ 
      
    //获取速度跟踪器,记录各个时刻的速度。并且添加当前的MotionEvent以记录更行速度值。 
    acquireVelocityTrackerAndAddMovement(ev); 
    ...... 
     /**
     * Shortcut the most recurring case: the user is in the dragging
     * state and he is moving his finger.  We want to intercept this
     * motion.
     * 最常见的需要拦截的情况:用户已经进入滑动状态,并且正在滑动手指。
     * 对这种情况直接进行拦截,执行onTouchEvent()继续执行滑动操作。
     **/ 
    final int action = ev.getAction(); 
    if ((action == MotionEvent.ACTION_MOVE) && 
            (mTouchState == TOUCH_STATE_SCROLLING)) { 
        return true; 
    } 
 
    switch (action & MotionEvent.ACTION_MASK) { 
        case MotionEvent.ACTION_MOVE: { 
             
            /**
             *  mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
             * whether the user has moved far enough from his original down touch.
             */ 
            /**
             * 当在这里接受到ACTION_MOVE时,说明mTouchState!=TOUCH_STATE_SCROLLING并且mIsBeingDragged的值应该为false,
             * 否则DragLayer就应该截获了MotionEvent用于实现拖拽。
             * 此时还没有进入滑动状态,当mActivePointerId == INVALID_POINTER时,也就是在此之前没有接收到任何touch事件。
             * 这种情况发生在Workspace变小时,也就是之前Workspace处于SPRING_LOADED状态。当出现这种情况时直接把当前的事件当作ACTION_DOWN进行处理。
             * 反之,则通过determineScrollingStart()尝试能够进入滑动状态。
             */ 
            if (mActivePointerId != INVALID_POINTER) { 
                determineScrollingStart(ev); 
                break; 
            } 
            // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN 
            // event. in that case, treat the first occurence of a move event as a ACTION_DOWN 
            // i.e. fall through to the next case (don't break) 
            // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events 
            // while it's small- this was causing a crash before we checked for INVALID_POINTER) 
        } 
 
        case MotionEvent.ACTION_DOWN: { 
            final float x = ev.getX(); 
            final float y = ev.getY(); 
            // Remember location of down touch 
            //记录按下的x的坐标值 
            mDownMotionX = x; 
            //记录前次发生touch时的坐标 
            mLastMotionX = x; 
            mLastMotionY = y; 
            //因为在ScrollBy时只能使用int,而记录的x和y都是float,会产生误差,故这里用mLastMotionXRemainder记录余数 
            //用于消除误差 
            mLastMotionXRemainder = 0; 
            //x方向上的总位移 
&nb

补充:移动开发 , Android ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,