天天看點

仿照Launcher的Workspace實作左右滑動切換

仿照Launcher的Workspace實作左右滑動切換

對于Launcher的桌面滑動大家應該都比較熟悉了,最好的體驗應該是可以随着手指的滑動而顯示不同位置的桌面,

比一般用ViewFlinger+動畫所實作的手勢切換頁面感覺良好多了~~~~

分析了一下Launcher中的WorkSpace,裡面有太多的代碼我們用不上了(拖拽,長按,,,),把裡面的備援代碼去掉得到實作滑動切換螢幕所必需的。。。。

建立一個ScrollLayout類,繼承自ViewGroup。

重寫onMeasure和onLayout兩個方法:

其中onMeasure方法中,得到ScrollLayout的布局方式(一般使用FILL_PARENT),然後再枚舉其中所有的子view,設定它們的布局(FILL_PARENT),這樣在ScrollLayout之中的每一個子view即為充滿螢幕可以滑動顯示的其中一頁。

在onLayout方法中,橫向畫出每一個子view,這樣所得到的view的高與螢幕高一緻,寬度為getChildCount()-1個螢幕寬度的view。

添加一個Scroller來平滑過渡各個頁面之間的切換,

重寫onInterceptTouchEvent和onTouchEvent來響應手指按下劃動時所需要捕獲的消息,例如劃動的速度,劃動的距離等。再配合使用scrollBy (int x, int y)方法得到慢速滑動小距離的時候,所需要顯示的内容。最後當手指起來時,根據劃動的速度與跨度來判斷是向左滑動一頁還是向右滑動一頁,確定每次使用者操作結束之後顯示的都是整體的一個子view.

ScrollLayout源碼:

view plain

  1. package com.yao_guet.test;      
  2. import Android.content.Context;      
  3. import android.graphics.Canvas;      
  4. import android.util.AttributeSet;      
  5. import android.util.Log;      
  6. import android.view.MotionEvent;      
  7. import android.view.VelocityTracker;      
  8. import android.view.View;      
  9. import android.view.ViewConfiguration;      
  10. import android.view.ViewGroup;      
  11. import android.widget.Scroller;      
  12. public class ScrollLayout extends ViewGroup {      
  13.     private static final String TAG = "ScrollLayout";      
  14.     private Scroller mScroller;      
  15.     private VelocityTracker mVelocityTracker;      
  16.     private int mCurScreen;      
  17.     private int mDefaultScreen = 0;      
  18.     private static final int TOUCH_STATE_REST = 0;      
  19.     private static final int TOUCH_STATE_SCROLLING = 1;      
  20.     private static final int SNAP_VELOCITY = 600;      
  21.     private int mTouchState = TOUCH_STATE_REST;      
  22.     private int mTouchSlop;      
  23.     private float mLastMotionX;      
  24.     private float mLastMotionY;      
  25.     public ScrollLayout(Context context, AttributeSet attrs) {      
  26.         this(context, attrs, 0);      
  27.         // TODO Auto-generated constructor stub      
  28.     }      
  29.     public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {      
  30.         super(context, attrs, defStyle);      
  31.         // TODO Auto-generated constructor stub      
  32.         mScroller = new Scroller(context);      
  33.         mCurScreen = mDefaultScreen;      
  34.         mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();      
  35.     }      
  36.     @Override      
  37.     protected void onLayout(boolean changed, int l, int t, int r, int b) {      
  38.         // TODO Auto-generated method stub      
  39.         if (changed) {      
  40.             int childLeft = 0;      
  41.             final int childCount = getChildCount();      
  42.             for (int i=0; i<childCount; i++) {      
  43.                 final View childView = getChildAt(i);      
  44.                 if (childView.getVisibility() != View.GONE) {      
  45.                     final int childWidth = childView.getMeasuredWidth();      
  46.                     childView.layout(childLeft, 0,       
  47.                             childLeft+childWidth, childView.getMeasuredHeight());      
  48.                     childLeft += childWidth;      
  49.                 }      
  50.             }      
  51.         }      
  52.     }      
  53.     @Override        
  54.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         
  55.         Log.e(TAG, "onMeasure");      
  56.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);         
  57.         final int width = MeasureSpec.getSize(widthMeasureSpec);         
  58.         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);         
  59.         if (widthMode != MeasureSpec.EXACTLY) {         
  60.             throw new IllegalStateException("ScrollLayout only canmCurScreen run at EXACTLY mode!");       
  61.         }         
  62.         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);         
  63.         if (heightMode != MeasureSpec.EXACTLY) {         
  64.             throw new IllegalStateException("ScrollLayout only can run at EXACTLY mode!");      
  65.         }         
  66.         // The children are given the same width and height as the scrollLayout         
  67.         final int count = getChildCount();         
  68.         for (int i = 0; i < count; i++) {         
  69.             getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);         
  70.         }         
  71.         // Log.e(TAG, "moving to screen "+mCurScreen);         
  72.         scrollTo(mCurScreen * width, 0);               
  73.     }        
  74.     public void snapToDestination() {      
  75.         final int screenWidth = getWidth();      
  76.         final int destScreen = (getScrollX()+ screenWidth/2)/screenWidth;      
  77.         snapToScreen(destScreen);      
  78.     }      
  79.     public void snapToScreen(int whichScreen) {      
  80.         // get the valid layout page      
  81.         whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1));      
  82.         if (getScrollX() != (whichScreen*getWidth())) {      
  83.             final int delta = whichScreen*getWidth()-getScrollX();      
  84.             mScroller.startScroll(getScrollX(), 0,       
  85.                     delta, 0, Math.abs(delta)*2);      
  86.             mCurScreen = whichScreen;      
  87.             invalidate();       // Redraw the layout      
  88.         }      
  89.     }      
  90.     public void setToScreen(int whichScreen) {      
  91.         whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1));      
  92.         mCurScreen = whichScreen;      
  93.         scrollTo(whichScreen*getWidth(), 0);      
  94.     }      
  95.     public int getCurScreen() {      
  96.         return mCurScreen;      
  97.     }      
  98.     @Override      
  99.     public void computeScroll() {      
  100.         // TODO Auto-generated method stub      
  101.         if (mScroller.computeScrollOffset()) {      
  102.             scrollTo(mScroller.getCurrX(), mScroller.getCurrY());      
  103.             postInvalidate();      
  104.         }      
  105.     }      
  106.     @Override      
  107.     public boolean onTouchEvent(MotionEvent event) {      
  108.         // TODO Auto-generated method stub      
  109.         if (mVelocityTracker == null) {      
  110.             mVelocityTracker = VelocityTracker.obtain();      
  111.         }      
  112.         mVelocityTracker.addMovement(event);      
  113.         final int action = event.getAction();      
  114.         final float x = event.getX();      
  115.         final float y = event.getY();      
  116.         switch (action) {      
  117.         case MotionEvent.ACTION_DOWN:      
  118.             Log.e(TAG, "event down!");      
  119.             if (!mScroller.isFinished()){      
  120.                 mScroller.abortAnimation();      
  121.             }      
  122.             mLastMotionX = x;      
  123.             break;      
  124.         case MotionEvent.ACTION_MOVE:      
  125.             int deltaX = (int)(mLastMotionX - x);      
  126.             mLastMotionX = x;      
  127.             scrollBy(deltaX, 0);      
  128.             break;      
  129.         case MotionEvent.ACTION_UP:      
  130.             Log.e(TAG, "event : up");         
  131.             // if (mTouchState == TOUCH_STATE_SCROLLING) {         
  132.             final VelocityTracker velocityTracker = mVelocityTracker;         
  133.             velocityTracker.computeCurrentVelocity(1000);         
  134.             int velocityX = (int) velocityTracker.getXVelocity();         
  135.             Log.e(TAG, "velocityX:"+velocityX);       
  136.             if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {         
  137.                 // Fling enough to move left         
  138.                 Log.e(TAG, "snap left");      
  139.                 snapToScreen(mCurScreen - 1);         
  140.             } else if (velocityX < -SNAP_VELOCITY         
  141.                     && mCurScreen < getChildCount() - 1) {         
  142.                 // Fling enough to move right         
  143.                 Log.e(TAG, "snap right");      
  144.                 snapToScreen(mCurScreen + 1);         
  145.             } else {         
  146.                 snapToDestination();         
  147.             }         
  148.             if (mVelocityTracker != null) {         
  149.                 mVelocityTracker.recycle();         
  150.                 mVelocityTracker = null;         
  151.             }         
  152.             // }         
  153.             mTouchState = TOUCH_STATE_REST;         
  154.             break;      
  155.         case MotionEvent.ACTION_CANCEL:      
  156.             mTouchState = TOUCH_STATE_REST;      
  157.             break;      
  158.         }      
  159.         return true;      
  160.     }      
  161.     @Override      
  162.     public boolean onInterceptTouchEvent(MotionEvent ev) {      
  163.         // TODO Auto-generated method stub      
  164.         Log.e(TAG, "onInterceptTouchEvent-slop:"+mTouchSlop);      
  165.         final int action = ev.getAction();      
  166.         if ((action == MotionEvent.ACTION_MOVE) &&       
  167.                 (mTouchState != TOUCH_STATE_REST)) {      
  168.             return true;      
  169.         }      
  170.         final float x = ev.getX();      
  171.         final float y = ev.getY();      
  172.         switch (action) {      
  173.         case MotionEvent.ACTION_MOVE:      
  174.             final int xDiff = (int)Math.abs(mLastMotionX-x);      
  175.             if (xDiff>mTouchSlop) {      
  176.                 mTouchState = TOUCH_STATE_SCROLLING;      
  177.             }      
  178.             break;      
  179.         case MotionEvent.ACTION_DOWN:      
  180.             mLastMotionX = x;      
  181.             mLastMotionY = y;      
  182.             mTouchState = mScroller.isFinished()? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;      
  183.             break;      
  184.         case MotionEvent.ACTION_CANCEL:      
  185.         case MotionEvent.ACTION_UP:      
  186.             mTouchState = TOUCH_STATE_REST;      
  187.             break;      
  188.         }      
  189.         return mTouchState != TOUCH_STATE_REST;      
  190.     }      
  191. }      

測試程式布局:

view plain

  1. <?xml version="1.0" encoding="utf-8"?>      
  2. <com.yao_guet.test.ScrollLayout      
  3.   xmlns:android="http://schemas.android.com/apk/res/android"      
  4.   android:id="@+id/ScrollLayoutTest"      
  5.   android:layout_width="fill_parent"      
  6.   android:layout_height="fill_parent">      
  7. <LinearLayout      
  8.   android:background="#FF00"      
  9.   android:layout_width="fill_parent"      
  10.   android:layout_height="fill_parent"></LinearLayout>      
  11. <FrameLayout      
  12.   android:background="#F0F0"      
  13.   android:layout_width="fill_parent"      
  14.   android:layout_height="fill_parent"></FrameLayout>      
  15. <FrameLayout      
  16.   android:background="#F00F"      
  17.   android:layout_width="fill_parent"      
  18.   android:layout_height="fill_parent">      
  19.   </FrameLayout>      
  20. <LinearLayout      
  21.   android:background="#FF00"      
  22.   android:layout_width="fill_parent"      
  23.   android:layout_height="fill_parent">      
  24.   <Button      
  25.     android:layout_width="wrap_content"      
  26.     android:layout_height="wrap_content"      
  27.     android:text="Button1" />      
  28.   </LinearLayout>      
  29. <LinearLayout      
  30.   android:layout_width="wrap_content"      
  31.   android:layout_height="wrap_content">      
  32.   <Button      
  33.     android:layout_width="wrap_content"      
  34.     android:layout_height="wrap_content"      
  35.     android:text="Button2" />      
  36.   </LinearLayout>      
  37. </com.yao_guet.test.ScrollLayout>