Chinaunix首页 | 论坛 | 博客
  • 博客访问: 450476
  • 博文数量: 145
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1060
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-22 11:52
个人简介

专注计算机技术: Linux Android 云计算 虚拟化 网络

文章分类

全部博文(145)

文章存档

2016年(3)

2015年(21)

2014年(75)

2013年(46)

我的朋友

分类: Android平台

2014-04-24 21:00:59

在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.         // The children are given the same width and height as the workspace
  13.         final int count = getChildCount();
  14.         for (int i = 0; i < count; i++) {
  15.             getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
  16.         }
  17.         //ADW: measure wallpaper when using old rendering
  18.         if(!lwpSupport){
  19.             if (mWallpaperLoaded) {
  20.              mWallpaperLoaded = false;
  21.              mWallpaperWidth = mWallpaperDrawable.getIntrinsicWidth();
  22.              mWallpaperHeight = mWallpaperDrawable.getIntrinsicHeight();
  23.             }

  24.             final int wallpaperWidth = mWallpaperWidth;
  25.             mWallpaperOffset = wallpaperWidth > width ? (count * width - wallpaperWidth) /
  26.              ((count - 1) * (float) width) : 1.0f;
  27.         }
  28.         if (mFirstLayout) {
  29.             scrollTo(mCurrentScreen * width, 0);
  30.             mScroller.startScroll(0, 0, mCurrentScreen * width, 0, 0);
  31.             if(lwpSupport)updateWallpaperOffset(width * (getChildCount() - 1));
  32.             mFirstLayout = false;
  33.         }
  34.         /*int max = 3;
  35.         int aW = getMeasuredWidth();
  36.         float w = aW / max;
  37.         maxPreviewWidth=(int) w;
  38.         maxPreviewHeight=(int) (getMeasuredHeight()*(w/getMeasuredWidth()));*/
  39.     }
在这里,得到屏幕的宽高,然后再枚举其中所有的子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.         //Wysie: If multitouch event is detected
  9.         if (multiTouchController.onTouchEvent(ev)) {
  10.             return false;
  11.         }

  12.         if (mLocked || mLauncher.isAllAppsVisible()) {
  13.             return true;
  14.         }

  15.         /*
  16.          * This method JUST determines whether we want to intercept the motion.
  17.          * If we return true, onTouchEvent will be called and we do the actual
  18.          * scrolling there.
  19.          */

  20.         /*
  21.          * Shortcut the most recurring case: the user is in the dragging
  22.          * state and he is moving his finger. We want to intercept this
  23.          * motion.
  24.          */
  25.         final int action = ev.getAction();
  26.         if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
  27.             return true;
  28.         }

  29.         final float x = ev.getX();
  30.         final float y = ev.getY();

  31.         switch (action) {
  32.             case MotionEvent.ACTION_MOVE:
  33.                 /*
  34.                  * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
  35.                  * whether the user has moved far enough from his original down touch.
  36.                  */

  37.                 /*
  38.                  * Locally do absolute value. mLastMotionX is set to the y value
  39.                  * of the down event.
  40.                  */
  41.                 final int xDiff = (int) Math.abs(x - mLastMotionX);
  42.                 final int yDiff = (int) Math.abs(y - mLastMotionY);

  43.                 final int touchSlop = mTouchSlop;
  44.                 boolean xMoved = xDiff > touchSlop;
  45.                 boolean yMoved = yDiff > touchSlop;
  46.                 if (xMoved || yMoved) {
  47.                     // If xDiff > yDiff means the finger path pitch is smaller than 45deg so we assume the user want to scroll X axis
  48.                     if (xDiff > yDiff) {
  49.                         // Scroll if the user moved far enough along the X axis
  50.                         mTouchState = TOUCH_STATE_SCROLLING;
  51.                         enableChildrenCache();

  52.                     }
  53.                     // 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
  54.                     else if (getOpenFolder()==null)
  55.                     {
  56.                         // 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.
  57.                         // Guess if the first Pointer where the user click belongs to where a scrollable widget is.
  58.                         mTouchedScrollableWidget = isWidgetAtLocationScrollable((int)mLastMotionX,(int)mLastMotionY);
  59.                         if (!mTouchedScrollableWidget)
  60.                         {
  61.          // Only y axis movement. So may be a Swipe down or up gesture
  62.          if ((y - mLastMotionY) > 0){
  63.              if(Math.abs(y-mLastMotionY)>(touchSlop*2))mTouchState = TOUCH_SWIPE_DOWN_GESTURE;
  64.          }else{
  65.              if(Math.abs(y-mLastMotionY)>(touchSlop*2))mTouchState = TOUCH_SWIPE_UP_GESTURE;
  66.          }
  67.                         }
  68.                     }
  69.                     // Either way, cancel any pending longpress
  70.                     if (mAllowLongPress) {
  71.                         mAllowLongPress = false;
  72.                         // Try canceling the long press. It could also have been scheduled
  73.                         // by a distant descendant, so use the mAllowLongPress flag to block
  74.                         // everything
  75.                         final View currentScreen = getChildAt(mCurrentScreen);
  76.                         currentScreen.cancelLongPress();
  77.                     }
  78.                 }
  79.                 break;

  80.             case MotionEvent.ACTION_DOWN:
  81.                 // Remember location of down touch
  82.                 mLastMotionX = x;
  83.                 mLastMotionY = y;
  84.                 mAllowLongPress = true;

  85.                 /*
  86.                  * If being flinged and user touches the screen, initiate drag;
  87.                  * otherwise don't. mScroller.isFinished should be false when
  88.                  * being flinged.
  89.                  */
  90.                 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
  91.                 break;

  92.             case MotionEvent.ACTION_CANCEL:
  93.             case MotionEvent.ACTION_UP:

  94.                 if (mTouchState != TOUCH_STATE_SCROLLING && mTouchState != TOUCH_SWIPE_DOWN_GESTURE && mTouchState != TOUCH_SWIPE_UP_GESTURE) {
  95.                     final CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
  96.                     if (!currentScreen.lastDownOnOccupiedCell()) {
  97.                         getLocationOnScreen(mTempCell);
  98.                         // Send a tap to the wallpaper if the last down was on empty space
  99.                         if(lwpSupport)
  100.                         mWallpaperManager.sendWallpaperCommand(getWindowToken(),
  101.                                 "android.wallpaper.tap",
  102.                                 mTempCell[0] + (int) ev.getX(),
  103.                                 mTempCell[1] + (int) ev.getY(), 0, null);
  104.                     }
  105.                 }
  106.                 // Release the drag
  107.                 clearChildrenCache();
  108.                 mTouchState = TOUCH_STATE_REST;
  109.                 mAllowLongPress = false;
  110.                 break;
  111.         }

  112.         /*
  113.          * The only time we want to intercept motion events is if we are in the
  114.          * drag mode.
  115.          */
  116.         return mTouchState != TOUCH_STATE_REST;
  117.     }
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.         if (mVelocityTracker == null) {
  10.             mVelocityTracker = VelocityTracker.obtain();
  11.         }
  12.         mVelocityTracker.addMovement(ev);

  13.         final int action = ev.getAction();
  14.         final float x = ev.getX();

  15.         switch (action) {
  16.         case MotionEvent.ACTION_DOWN:
  17.             /*
  18.              * If being flinged and user touches, stop the fling. isFinished
  19.              * will be false if being flinged.
  20.              */
  21.             if (!mScroller.isFinished()) {
  22.                 mScroller.abortAnimation();
  23.             }

  24.             // Remember where the motion event started
  25.             mLastMotionX = x;
  26.             break;
  27.         case MotionEvent.ACTION_MOVE:
  28.             if (mTouchState == TOUCH_STATE_SCROLLING) {
  29.                 // Scroll to follow the motion event
  30.                 final int deltaX = (int) (mLastMotionX - x);
  31.                 mLastMotionX = x;

  32.                 if (deltaX < 0) {
  33.                     if (mScrollX > -mScrollingBounce) {
  34.                         scrollBy(Math.min(deltaX,mScrollingBounce), 0);
  35.                         if(lwpSupport)updateWallpaperOffset();
  36.                         if(mLauncher.getDesktopIndicator()!=null)mLauncher.getDesktopIndicator().indicate((float)getScrollX()/(float)(getChildCount()*getWidth()));
  37.                     }
  38.                 } else if (deltaX > 0) {
  39.                     final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -
  40.                             mScrollX - getWidth()+mScrollingBounce;
  41.                     if (availableToScroll > 0) {
  42.                         scrollBy(deltaX, 0);
  43.                         if(lwpSupport)updateWallpaperOffset();
  44.                         if(mLauncher.getDesktopIndicator()!=null)mLauncher.getDesktopIndicator().indicate((float)getScrollX()/(float)(getChildCount()*getWidth()));
  45.                     }
  46.                 }
  47.             }
  48.             break;
  49.         case MotionEvent.ACTION_UP:
  50.             if (mTouchState == TOUCH_STATE_SCROLLING) {
  51.                 final VelocityTracker velocityTracker = mVelocityTracker;
  52.                 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
  53.                 int velocityX = (int) velocityTracker.getXVelocity();

  54.                 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
  55.                     // Fling hard enough to move left
  56.                     snapToScreen(mCurrentScreen - 1);
  57.                 } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
  58.                     // Fling hard enough to move right
  59.                     snapToScreen(mCurrentScreen + 1);
  60.                 } else {
  61.                     snapToDestination();
  62.                 }

  63.                 if (mVelocityTracker != null) {
  64.                     mVelocityTracker.recycle();
  65.                     mVelocityTracker = null;
  66.                 }
  67.             } else if (mTouchState == TOUCH_SWIPE_DOWN_GESTURE )
  68.             {
  69.                 mLauncher.fireSwipeDownAction();
  70.             } else if (mTouchState == TOUCH_SWIPE_UP_GESTURE )
  71.             {
  72.                 mLauncher.fireSwipeUpAction();
  73.             }
  74.             mTouchState = TOUCH_STATE_REST;
  75.             break;
  76.         case MotionEvent.ACTION_CANCEL:
  77.             mTouchState = TOUCH_STATE_REST;
  78.         }

  79.         return true;
  80.     }

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

有需要代码的朋友,请到下载即可。



阅读(749) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~