在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;
- }