launcher修改--左右滑动屏幕切换源码追踪

时间:2022-11-25 00:05:09

             在android的源代码中,屏幕之间的跳转是如何实现的呢?在workspace.java中开始。在这个类中,为实现屏幕切换主要重写了以下几个方法:onMeasure()、onLayout()、onInterceptTouchEvent()、onTouchEvent()方法,另外还是用了CustomScroller mScroller来平滑过渡各个页面之间的切换。

          首先,我们看一下onMeasure()方法,根据字面意思,就是测量,主要负责测量各个控件的高度和宽度:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
}
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
}

// The children are given the same width and height as the workspace
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
//ADW: measure wallpaper when using old rendering
if(!lwpSupport){
if (mWallpaperLoaded) {
mWallpaperLoaded = false;
mWallpaperWidth = mWallpaperDrawable.getIntrinsicWidth();
mWallpaperHeight = mWallpaperDrawable.getIntrinsicHeight();
}

final int wallpaperWidth = mWallpaperWidth;
mWallpaperOffset = wallpaperWidth > width ? (count * width - wallpaperWidth) /
((count - 1) * (float) width) : 1.0f;
}
if (mFirstLayout) {
scrollTo(mCurrentScreen * width, 0);
mScroller.startScroll(0, 0, mCurrentScreen * width, 0, 0);
if(lwpSupport)updateWallpaperOffset(width * (getChildCount() - 1));
mFirstLayout = false;
}
/*int max = 3;
int aW = getMeasuredWidth();
float w = aW / max;
maxPreviewWidth=(int) w;
maxPreviewHeight=(int) (getMeasuredHeight()*(w/getMeasuredWidth()));*/
}
在这里,得到屏幕的宽高, 然后再枚举其中所有的子view,设置它们的布局(使他们的高和父控件一样),这样每一个子view就是充满屏幕可以滑动显示的其中一页。
       下面是 onLayout()方法:

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        int childLeft = 0;        final int count = getChildCount();        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() != View.GONE) {                final int childWidth = child.getMeasuredWidth();                child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());                childLeft += childWidth;            }        }        //ADW:updateWallpaperoffset        if(lwpSupport){        if(mWallpaperScroll)        updateWallpaperOffset();        else        centerWallpaperOffset();        }    }

        onLayout方法中,横向画出每一个子view,view的高与屏幕高一致,宽度为getChildCount()-1个屏幕宽度的view。

         再看一下onInterceptTouchEvent()方法:

public boolean onInterceptTouchEvent(MotionEvent ev) {    if(mStatus==SENSE_OPEN){    if(ev.getAction()==MotionEvent.ACTION_DOWN){    findClickedPreview(ev.getX(),ev.getY());    }    return true;    }        //Wysie: If multitouch event is detected        if (multiTouchController.onTouchEvent(ev)) {            return false;        }        if (mLocked || mLauncher.isAllAppsVisible()) {            return true;        }        /*         * 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.         */        /*         * Shortcut the most recurring case: the user is in the dragging         * state and he is moving his finger.  We want to intercept this         * motion.         */        final int action = ev.getAction();        if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {            return true;        }        final float x = ev.getX();        final float y = ev.getY();        switch (action) {            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.                 */                /*                 * Locally do absolute value. mLastMotionX is set to the y value                 * of the down event.                 */                final int xDiff = (int) Math.abs(x - mLastMotionX);                final int yDiff = (int) Math.abs(y - mLastMotionY);                final int touchSlop = mTouchSlop;                boolean xMoved = xDiff > touchSlop;                boolean yMoved = yDiff > touchSlop;                if (xMoved || yMoved) {                    // If xDiff > yDiff means the finger path pitch is smaller than 45deg so we assume the user want to scroll X axis                    if (xDiff > yDiff) {                        // Scroll if the user moved far enough along the X axis                        mTouchState = TOUCH_STATE_SCROLLING;                        enableChildrenCache();                    }                    // If yDiff > xDiff means the finger path pitch is bigger than 45deg so we assume the user want to either scroll Y or Y-axis gesture                    else if (getOpenFolder()==null)                    {                    // As x scrolling is left untouched (more or less untouched;)), every gesture should start by dragging in Y axis. In fact I only consider useful, swipe up and down.                    // Guess if the first Pointer where the user click belongs to where a scrollable widget is.                mTouchedScrollableWidget = isWidgetAtLocationScrollable((int)mLastMotionX,(int)mLastMotionY);                    if (!mTouchedScrollableWidget)                    {                    // Only y axis movement. So may be a Swipe down or up gesture                    if ((y - mLastMotionY) > 0){                    if(Math.abs(y-mLastMotionY)>(touchSlop*2))mTouchState = TOUCH_SWIPE_DOWN_GESTURE;                    }else{                    if(Math.abs(y-mLastMotionY)>(touchSlop*2))mTouchState = TOUCH_SWIPE_UP_GESTURE;                    }                    }                    }                    // Either way, cancel any pending longpress                    if (mAllowLongPress) {                        mAllowLongPress = false;                        // Try canceling the long press. It could also have been scheduled                        // by a distant descendant, so use the mAllowLongPress flag to block                        // everything                        final View currentScreen = getChildAt(mCurrentScreen);                        currentScreen.cancelLongPress();                    }                }                break;            case MotionEvent.ACTION_DOWN:                // Remember location of down touch                mLastMotionX = x;                mLastMotionY = y;                mAllowLongPress = true;                /*                 * If being flinged and user touches the screen, initiate drag;                 * otherwise don't.  mScroller.isFinished should be false when                 * being flinged.                 */                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;                break;            case MotionEvent.ACTION_CANCEL:            case MotionEvent.ACTION_UP:            if (mTouchState != TOUCH_STATE_SCROLLING && mTouchState != TOUCH_SWIPE_DOWN_GESTURE && mTouchState != TOUCH_SWIPE_UP_GESTURE) {                    final CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);                    if (!currentScreen.lastDownOnOccupiedCell()) {                        getLocationOnScreen(mTempCell);                        // Send a tap to the wallpaper if the last down was on empty space                        if(lwpSupport)                        mWallpaperManager.sendWallpaperCommand(getWindowToken(),                                "android.wallpaper.tap",                                mTempCell[0] + (int) ev.getX(),                                mTempCell[1] + (int) ev.getY(), 0, null);                    }                }                // Release the drag                clearChildrenCache();                mTouchState = TOUCH_STATE_REST;                mAllowLongPress = false;                break;        }        /*         * The only time we want to intercept motion events is if we are in the         * drag mode.         */        return mTouchState != TOUCH_STATE_REST;    }

onInterceptTouchEvent()方法和下面的onTouchEvent()主要是来响应手指按下划动时所需要捕获的消息,例如划动的速度,划动的距离等。再配合使用scrollBy (int x, int y)方法得到慢速滑动小距离的时候,所需要显示的内容。最后当手指起来时,根据划动的速度与跨度来判断是向左滑动一页还是向右滑动一页,确保每次用户操作结束之后显示的都是整体的一个子view.

 public boolean onTouchEvent(MotionEvent ev) {        //Wysie: If multitouch event is detected        /*if (multiTouchController.onTouchEvent(ev)) {            return false;        }*/        if (mLocked || mLauncher.isAllAppsVisible() || mSensemode) {            return true;        }        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(ev);        final int action = ev.getAction();        final float x = ev.getX();        switch (action) {        case MotionEvent.ACTION_DOWN:            /*             * If being flinged and user touches, stop the fling. isFinished             * will be false if being flinged.             */            if (!mScroller.isFinished()) {                mScroller.abortAnimation();            }            // Remember where the motion event started            mLastMotionX = x;            break;        case MotionEvent.ACTION_MOVE:            if (mTouchState == TOUCH_STATE_SCROLLING) {                // Scroll to follow the motion event                final int deltaX = (int) (mLastMotionX - x);                mLastMotionX = x;                if (deltaX < 0) {                    if (mScrollX > -mScrollingBounce) {                        scrollBy(Math.min(deltaX,mScrollingBounce), 0);                        if(lwpSupport)updateWallpaperOffset();                        if(mLauncher.getDesktopIndicator()!=null)mLauncher.getDesktopIndicator().indicate((float)getScrollX()/(float)(getChildCount()*getWidth()));                    }                } else if (deltaX > 0) {                    final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -                            mScrollX - getWidth()+mScrollingBounce;                    if (availableToScroll > 0) {                        scrollBy(deltaX, 0);                        if(lwpSupport)updateWallpaperOffset();                        if(mLauncher.getDesktopIndicator()!=null)mLauncher.getDesktopIndicator().indicate((float)getScrollX()/(float)(getChildCount()*getWidth()));                    }                }            }            break;        case MotionEvent.ACTION_UP:            if (mTouchState == TOUCH_STATE_SCROLLING) {                final VelocityTracker velocityTracker = mVelocityTracker;                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);                int velocityX = (int) velocityTracker.getXVelocity();                if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {                    // Fling hard enough to move left                    snapToScreen(mCurrentScreen - 1);                } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {                    // Fling hard enough to move right                    snapToScreen(mCurrentScreen + 1);                } else {                    snapToDestination();                }                if (mVelocityTracker != null) {                    mVelocityTracker.recycle();                    mVelocityTracker = null;                }            } else if (mTouchState == TOUCH_SWIPE_DOWN_GESTURE )            {            mLauncher.fireSwipeDownAction();            } else if (mTouchState == TOUCH_SWIPE_UP_GESTURE )            {            mLauncher.fireSwipeUpAction();            }            mTouchState = TOUCH_STATE_REST;            break;        case MotionEvent.ACTION_CANCEL:            mTouchState = TOUCH_STATE_REST;        }        return true;    }

以上就是launcher中左右滑动屏幕切换源码。

有需要代码的朋友,请到http://download.csdn.net/detail/aomandeshangxiao/4063177下载即可。