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

本文详细解析了Android系统中屏幕之间平滑切换的源代码实现。通过重写关键方法如onMeasure、onLayout及触控相关的onInterceptTouchEvent和onTouchEvent等,实现了屏幕间的流畅切换效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

首先,我们看一下onMeasure()方法:

  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  2.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  3.         final int width = MeasureSpec.getSize(widthMeasureSpec);  
  4.         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  5.         if (widthMode != MeasureSpec.EXACTLY) {  
  6.             throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");  
  7.         }  
  8.         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  9.         if (heightMode != MeasureSpec.EXACTLY) {  
  10.             throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");  
  11.         }  
  12.   
  13.         // The children are given the same width and height as the workspace   
  14.         final int count = getChildCount();  
  15.         for (int i = 0; i < count; i++) {  
  16.             getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);  
  17.         }  
  18.         //ADW: measure wallpaper when using old rendering   
  19.         if(!lwpSupport){  
  20.             if (mWallpaperLoaded) {  
  21.                 mWallpaperLoaded = false;  
  22.                 mWallpaperWidth = mWallpaperDrawable.getIntrinsicWidth();  
  23.                 mWallpaperHeight = mWallpaperDrawable.getIntrinsicHeight();  
  24.             }  
  25.   
  26.             final int wallpaperWidth = mWallpaperWidth;  
  27.             mWallpaperOffset = wallpaperWidth > width ? (count * width - wallpaperWidth) /  
  28.                     ((count - 1) * (float) width) : 1.0f;  
  29.         }  
  30.         if (mFirstLayout) {  
  31.             scrollTo(mCurrentScreen * width, 0);  
  32.             mScroller.startScroll(00, mCurrentScreen * width, 00);  
  33.             if(lwpSupport)updateWallpaperOffset(width * (getChildCount() - 1));  
  34.             mFirstLayout = false;  
  35.         }  
  36.         /*int max = 3; 
  37.         int aW = getMeasuredWidth(); 
  38.         float w = aW / max; 
  39.         maxPreviewWidth=(int) w; 
  40.         maxPreviewHeight=(int) (getMeasuredHeight()*(w/getMeasuredWidth()));*/  
  41.     }  
在这里,得到屏幕的宽高, 然后再枚举其中所有的子view,设置它们的布局(使他们的高和父控件一样),这样每一个子view就是充满屏幕可以滑动显示的其中一页。
       下面是 onLayout()方法:
  1. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
  2.         int childLeft = 0;  
  3.         final int count = getChildCount();  
  4.         for (int i = 0; i < count; i++) {  
  5.             final View child = getChildAt(i);  
  6.             if (child.getVisibility() != View.GONE) {  
  7.                 final int childWidth = child.getMeasuredWidth();  
  8.                 child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());  
  9.                 childLeft += childWidth;  
  10.             }  
  11.         }  
  12.         //ADW:updateWallpaperoffset   
  13.         if(lwpSupport){  
  14.             if(mWallpaperScroll)  
  15.                 updateWallpaperOffset();  
  16.             else  
  17.                 centerWallpaperOffset();  
  18.         }  
  19.     }  
onLayout方法中,横向画出每一个子view,view的高与屏幕高一致,宽度为getChildCount()-1个屏幕宽度的view。

再看一下onInterceptTouchEvent()方法:

  1. public boolean onInterceptTouchEvent(MotionEvent ev) {  
  2.         if(mStatus==SENSE_OPEN){  
  3.             if(ev.getAction()==MotionEvent.ACTION_DOWN){  
  4.                 findClickedPreview(ev.getX(),ev.getY());  
  5.             }  
  6.             return true;  
  7.         }  
  8.   
  9.         //Wysie: If multitouch event is detected   
  10.         if (multiTouchController.onTouchEvent(ev)) {  
  11.             return false;  
  12.         }  
  13.   
  14.         if (mLocked || mLauncher.isAllAppsVisible()) {  
  15.             return true;  
  16.         }  
  17.   
  18.         /* 
  19.          * This method JUST determines whether we want to intercept the motion. 
  20.          * If we return true, onTouchEvent will be called and we do the actual 
  21.          * scrolling there. 
  22.          */  
  23.   
  24.         /* 
  25.          * Shortcut the most recurring case: the user is in the dragging 
  26.          * state and he is moving his finger.  We want to intercept this 
  27.          * motion. 
  28.          */  
  29.         final int action = ev.getAction();  
  30.         if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {  
  31.             return true;  
  32.         }  
  33.   
  34.         final float x = ev.getX();  
  35.         final float y = ev.getY();  
  36.   
  37.         switch (action) {  
  38.             case MotionEvent.ACTION_MOVE:  
  39.                 /* 
  40.                  * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 
  41.                  * whether the user has moved far enough from his original down touch. 
  42.                  */  
  43.   
  44.                 /* 
  45.                  * Locally do absolute value. mLastMotionX is set to the y value 
  46.                  * of the down event. 
  47.                  */  
  48.                 final int xDiff = (int) Math.abs(x - mLastMotionX);  
  49.                 final int yDiff = (int) Math.abs(y - mLastMotionY);  
  50.   
  51.                 final int touchSlop = mTouchSlop;  
  52.                 boolean xMoved = xDiff > touchSlop;  
  53.                 boolean yMoved = yDiff > touchSlop;  
  54.                 if (xMoved || yMoved) {  
  55.                     // If xDiff > yDiff means the finger path pitch is smaller than 45deg so we assume the user want to scroll X axis   
  56.                     if (xDiff > yDiff) {  
  57.                         // Scroll if the user moved far enough along the X axis   
  58.                         mTouchState = TOUCH_STATE_SCROLLING;  
  59.                         enableChildrenCache();  
  60.   
  61.                     }  
  62.                     // 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   
  63.                     else if (getOpenFolder()==null)  
  64.                     {  
  65.                         // 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.   
  66.                         // Guess if the first Pointer where the user click belongs to where a scrollable widget is.   
  67.                         mTouchedScrollableWidget = isWidgetAtLocationScrollable((int)mLastMotionX,(int)mLastMotionY);  
  68.                         if (!mTouchedScrollableWidget)  
  69.                         {  
  70.                             // Only y axis movement. So may be a Swipe down or up gesture   
  71.                             if ((y - mLastMotionY) > 0){  
  72.                                 if(Math.abs(y-mLastMotionY)>(touchSlop*2))mTouchState = TOUCH_SWIPE_DOWN_GESTURE;  
  73.                             }else{  
  74.                                 if(Math.abs(y-mLastMotionY)>(touchSlop*2))mTouchState = TOUCH_SWIPE_UP_GESTURE;  
  75.                             }  
  76.                         }  
  77.                     }  
  78.                     // Either way, cancel any pending longpress   
  79.                     if (mAllowLongPress) {  
  80.                         mAllowLongPress = false;  
  81.                         // Try canceling the long press. It could also have been scheduled   
  82.                         // by a distant descendant, so use the mAllowLongPress flag to block   
  83.                         // everything   
  84.                         final View currentScreen = getChildAt(mCurrentScreen);  
  85.                         currentScreen.cancelLongPress();  
  86.                     }  
  87.                 }  
  88.                 break;  
  89.   
  90.             case MotionEvent.ACTION_DOWN:  
  91.                 // Remember location of down touch   
  92.                 mLastMotionX = x;  
  93.                 mLastMotionY = y;  
  94.                 mAllowLongPress = true;  
  95.   
  96.                 /* 
  97.                  * If being flinged and user touches the screen, initiate drag; 
  98.                  * otherwise don't.  mScroller.isFinished should be false when 
  99.                  * being flinged. 
  100.                  */  
  101.                 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;  
  102.                 break;  
  103.   
  104.             case MotionEvent.ACTION_CANCEL:  
  105.             case MotionEvent.ACTION_UP:  
  106.   
  107.                 if (mTouchState != TOUCH_STATE_SCROLLING && mTouchState != TOUCH_SWIPE_DOWN_GESTURE && mTouchState != TOUCH_SWIPE_UP_GESTURE) {  
  108.                     final CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);  
  109.                     if (!currentScreen.lastDownOnOccupiedCell()) {  
  110.                         getLocationOnScreen(mTempCell);  
  111.                         // Send a tap to the wallpaper if the last down was on empty space   
  112.                         if(lwpSupport)  
  113.                         mWallpaperManager.sendWallpaperCommand(getWindowToken(),  
  114.                                 "Android.wallpaper.tap",  
  115.                                 mTempCell[0] + (int) ev.getX(),  
  116.                                 mTempCell[1] + (int) ev.getY(), 0null);  
  117.                     }  
  118.                 }  
  119.                 // Release the drag   
  120.                 clearChildrenCache();  
  121.                 mTouchState = TOUCH_STATE_REST;  
  122.                 mAllowLongPress = false;  
  123.                 break;  
  124.         }  
  125.   
  126.         /* 
  127.          * The only time we want to intercept motion events is if we are in the 
  128.          * drag mode. 
  129.          */  
  130.         return mTouchState != TOUCH_STATE_REST;  
  131.     }  
  132. onInterceptTouchEvent()方法和下面的onTouchEvent()主要是来响应手指按下划动时所需要捕获的消息,例如划动的速度,划动的距离等。再配合使用scrollBy (int x, int y)方法得到慢速滑动小距离的时候,所需要显示的内容。最后当手指起来时,根据划动的速度与跨度来判断是向左滑动一页还是向右滑动一页,确保每次用户操作结束之后显示的都是整体的一个子view.
    1. public boolean onTouchEvent(MotionEvent ev) {  
    2.        //Wysie: If multitouch event is detected   
    3.        /*if (multiTouchController.onTouchEvent(ev)) { 
    4.            return false; 
    5.        }*/  
    6.        if (mLocked || mLauncher.isAllAppsVisible() || mSensemode) {  
    7.            return true;  
    8.        }  
    9.   
    10.        if (mVelocityTracker == null) {  
    11.            mVelocityTracker = VelocityTracker.obtain();  
    12.        }  
    13.        mVelocityTracker.addMovement(ev);  
    14.   
    15.        final int action = ev.getAction();  
    16.        final float x = ev.getX();  
    17.   
    18.        switch (action) {  
    19.        case MotionEvent.ACTION_DOWN:  
    20.            /* 
    21.             * If being flinged and user touches, stop the fling. isFinished 
    22.             * will be false if being flinged. 
    23.             */  
    24.            if (!mScroller.isFinished()) {  
    25.                mScroller.abortAnimation();  
    26.            }  
    27.   
    28.            // Remember where the motion event started   
    29.            mLastMotionX = x;  
    30.            break;  
    31.        case MotionEvent.ACTION_MOVE:  
    32.            if (mTouchState == TOUCH_STATE_SCROLLING) {  
    33.                // Scroll to follow the motion event   
    34.                final int deltaX = (int) (mLastMotionX - x);  
    35.                mLastMotionX = x;  
    36.   
    37.                if (deltaX < 0) {  
    38.                    if (mScrollX > -mScrollingBounce) {  
    39.                        scrollBy(Math.min(deltaX,mScrollingBounce), 0);  
    40.                        if(lwpSupport)updateWallpaperOffset();  
    41.                        if(mLauncher.getDesktopIndicator()!=null)mLauncher.getDesktopIndicator().indicate((float)getScrollX()/(float)(getChildCount()*getWidth()));  
    42.                    }  
    43.                } else if (deltaX > 0) {  
    44.                    final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -  
    45.                            mScrollX - getWidth()+mScrollingBounce;  
    46.                    if (availableToScroll > 0) {  
    47.                        scrollBy(deltaX, 0);  
    48.                        if(lwpSupport)updateWallpaperOffset();  
    49.                        if(mLauncher.getDesktopIndicator()!=null)mLauncher.getDesktopIndicator().indicate((float)getScrollX()/(float)(getChildCount()*getWidth()));  
    50.                    }  
    51.                }  
    52.            }  
    53.            break;  
    54.        case MotionEvent.ACTION_UP:  
    55.            if (mTouchState == TOUCH_STATE_SCROLLING) {  
    56.                final VelocityTracker velocityTracker = mVelocityTracker;  
    57.                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);  
    58.                int velocityX = (int) velocityTracker.getXVelocity();  
    59.   
    60.                if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {  
    61.                    // Fling hard enough to move left   
    62.                    snapToScreen(mCurrentScreen - 1);  
    63.                } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {  
    64.                    // Fling hard enough to move right   
    65.                    snapToScreen(mCurrentScreen + 1);  
    66.                } else {  
    67.                    snapToDestination();  
    68.                }  
    69.   
    70.                if (mVelocityTracker != null) {  
    71.                    mVelocityTracker.recycle();  
    72.                    mVelocityTracker = null;  
    73.                }  
    74.            } else if (mTouchState == TOUCH_SWIPE_DOWN_GESTURE )  
    75.            {  
    76.             mLauncher.fireSwipeDownAction();  
    77.            } else if (mTouchState == TOUCH_SWIPE_UP_GESTURE )  
    78.            {  
    79.             mLauncher.fireSwipeUpAction();  
    80.            }  
    81.            mTouchState = TOUCH_STATE_REST;  
    82.            break;  
    83.        case MotionEvent.ACTION_CANCEL:  
    84.            mTouchState = TOUCH_STATE_REST;  
    85.        }  
    86.   
    87.        return true;  
    88.    }  
    以上就是launcher中左右滑动屏幕切换源码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值