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

在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中左右滑动屏幕切换源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值