现在的位置: 首页 > 综合 > 正文

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

2013年08月14日 ⁄ 综合 ⁄ 共 8143字 ⁄ 字号 评论关闭

             在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下载即可。

抱歉!评论已关闭.