Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 如果寫一個android桌面滑動切換屏幕的控件(一)

如果寫一個android桌面滑動切換屏幕的控件(一)

編輯:關於Android編程

首先這個控件應該是繼承ViewGroup:

初始化:

public class MyGroup extends ViewGroup{

	private Scroller mScroller;
	private float mOriMotionX;
	private float mLastMotionX;
	private VelocityTracker mVelocityTracker;
	private int mTouchState = TOUCH_STATE_REST;
	private static final int TOUCH_STATE_REST = 0;
	private int mTouchSlop;
	private int mMaximumVelocity;
	private static final int TOUCH_STATE_SCROLLING = 1;
	private float mLastDownX;
	private static final int DEFAULT_VALUE = 1000;
	private int mNextScreen = -1;
	private boolean mFlagLimitUp = false;
	private static final int SNAP_VELOCITY = 700;

	
	private int mCurrentScreen;
	public MyGroup(Context context, AttributeSet attrs) {
		super(context, attrs);
		initWorkspace();
	}
	
	private void initWorkspace() {
		mScroller = new Scroller(getContext());
		setCurrentScreen(0);

		final ViewConfiguration configuration = ViewConfiguration
				.get(getContext());
		mTouchSlop = configuration.getScaledTouchSlop();//這個是定義控件在scroll的最小像素距離
		mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); //速率,fling的一個以每秒滑動多少像素的值
	}


先重寫onmeasure:

@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

		final int width = MeasureSpec.getSize(widthMeasureSpec);
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
		}

	}

onLayout:

@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
		int paddingleft = 0;
		int paddingTop = 0;
		int childLeft = paddingleft;
		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();
				final int childHeight = child.getMeasuredHeight() ;

				child.layout(childLeft, paddingTop, childLeft + childWidth,
						childHeight + paddingTop);
				childLeft += child.getMeasuredWidth();  //下個child的左邊距和第一個child的左邊距之間的距離正好是第一個child的width
			}
		}
		
	}

然後寫View的touch事件:

onInterceptTouchEvent只有返回false事件才會傳遞給控件裡的view,就是view的ontouch事件才可以捕捉
View裡的onTouchEvent返回為true,才能執行多次touch事件,事件才能得了傳遞

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		final int action = ev.getAction();
		//如果為move事件,mTouchState為TOUCH_STATE_REST為靜止狀態,這個是防止子控件在滑動時又用手指去滑,這種情況下不響應這個事件
		if ((action == MotionEvent.ACTION_MOVE)
				&& (mTouchState != TOUCH_STATE_REST)) {
			return true;
		}

		final float x = ev.getX();

		switch (action) {
			case MotionEvent.ACTION_MOVE:
				final int xDiff = (int) Math.abs(x - mLastMotionX);
				final int touchSlop = mTouchSlop;
				boolean xMoved = xDiff > touchSlop;
				//如果xMoved為true表示手指在滑動
				if (xMoved) {
					mTouchState = TOUCH_STATE_SCROLLING;
				}
				break;
			case MotionEvent.ACTION_DOWN:
				mLastMotionX = x;
				//mScroller.isFinished() 為true表示滑動結束了,這時候我們把狀態置為TOUCH_STATE_REST
				mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
						: TOUCH_STATE_SCROLLING;
				break;

			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:
				mTouchState = TOUCH_STATE_REST;
				break;
			default:
				break;
			}
		
		//如果不是在靜止狀態,都返回true,這樣事件就不會傳遞給onTouchEvent了
		return mTouchState != TOUCH_STATE_REST;
	}

在滑動的時候返回true的原因是這時候不需要響應裡面控件的ontouch事件

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		if (mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain();
		}
		mVelocityTracker.addMovement(ev);

		int mScrollX = this.getScrollX(); //mScrollX表示X軸上的距離,往左滑動為正,這個時候屏幕向右移動

		final int action = ev.getAction();
		final float x = ev.getX();
		final float y = ev.getY();
		switch (action) {
			case MotionEvent.ACTION_DOWN:
				mOriMotionX = x;
				mLastMotionX = x;
				if (!mScroller.isFinished()) {
					mScroller.abortAnimation();
				}
				mOriMotionX = x;
				mLastMotionX = x;
				mLastDownX = x;
				return true;
			case MotionEvent.ACTION_MOVE:
				System.out.println("====action move mScrollX="+mScrollX);
				final int buffer = getWidth() / 2; //這個表示在第一頁或是最後一頁還可以滑動半個屏幕
				//如果是往後滑動,屏幕向前,那麼mLastMotionX是比x大的,deltaX是正的
				int deltaX = (int) (mLastMotionX - x);
				mLastMotionX = x;
				System.out.println("=====deltaX="+deltaX);
				//deltaX<0表示往前滑動
				if (deltaX < 0) {
					//這個是往右滑動,屏幕向左移動
					scrollBy(Math.max(-mScrollX - buffer, deltaX), 0);
				}else{
					int availableToScroll = 0;
					if (getChildCount() > 0) { //此時Workspace上可能未加任何item,count == 0
						System.out.println("====rihgt="+(getChildAt(
			                            getChildCount() - 1).getRight())+"avail="+(getChildAt(
			                            getChildCount() - 1).getRight()- mScrollX - getWidth()
			                            ));
						//getChildAt(getChildCount() - 1).getRight()為所有的view加一起的寬度,這裡加了3個view,一個view為1080,則這個值為3240
					    availableToScroll = getChildAt(
			                            getChildCount() - 1).getRight()
			                            - mScrollX - getWidth();
					    //availableToScroll + buffer可以滑動的最大距離,deltax為滑動的距離
					    scrollBy(Math.min(availableToScroll + buffer, deltaX), 0);
					}
				}
				
			return true;
			case MotionEvent.ACTION_UP:
				final VelocityTracker velocityTracker = mVelocityTracker;

				velocityTracker.computeCurrentVelocity(DEFAULT_VALUE,
						mMaximumVelocity);
				int velocityX = (int) velocityTracker.getXVelocity();
				//velocityX為手指滑動的速率,我們會跟給定值SNAP_VELOCITY做比較
				if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
					//  這個時候是手指往前滑動,屏幕是向後移動
					snapToScreen(mCurrentScreen - 1);
				} else if (velocityX < -SNAP_VELOCITY
						&& mCurrentScreen < getChildCount() - 1) {
					//  move right
					snapToScreen(mCurrentScreen + 1);
				} else {
					snapToDestination(mLastMotionX < mOriMotionX);
				}
				if (mVelocityTracker != null) {
					mVelocityTracker.recycle();
					mVelocityTracker = null;
				}
				mTouchState = TOUCH_STATE_REST;
				if (Math.abs(mLastDownX - x) > 10) {
					return true;
				}
				return false;
			case MotionEvent.ACTION_CANCEL:
				mTouchState = TOUCH_STATE_REST;
				return false;
			default:
				break;
			}

		return true;
	}

/**滑動的距離,離屏寬幾分之一時,就開始執行換屏動作。*/
	/**
	 * snapToDestination.
	 * mLastMotionX < mOriMotionX (mLastMotion < mOriMotionX)表示這個是手向後滑動,但屏幕是往前的,反之是向前
	 * forward為true為往前劃動,這時將scrollX加上三分之二的屏幕的寬度
	 * scrollX / screenWidth 來決定當前在哪個屏幕
	 * @param forward 是前進還是後退.
	 */
	public void snapToDestination(boolean forward) {
		final int screenWidth = getWidth();
		int scrollX = getScrollX();

		if (forward) {
		    scrollX += screenWidth - screenWidth / 3;
		} else { 
		    scrollX += screenWidth / 3;
		}
		System.out.println("======screenWidth="+screenWidth+"scrollX / screenWidth="+(scrollX / screenWidth));
		snapToScreen(scrollX / screenWidth);
	}
	
	/**
	 * 如果計算要滑動的距離:(whichScreen * getWidth())為滑動後的X坐標,this.getScrollX()為當前的坐標,兩者相減為滑動的距離
	 * Math.abs(delta) * 2為滑動的持續時間
	 */
	public void snapToScreen(int whichScreen) {
		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
		boolean changingScreens = whichScreen != mCurrentScreen;

		mNextScreen = whichScreen;
		int mScrollX = this.getScrollX();
		final int newX = whichScreen * getWidth();
		final int delta = newX - mScrollX;
		System.out.println("====snapToScreen delta="+delta);
		mScroller.startScroll(mScrollX, 0, delta, 0, Math.abs(delta) * 2);
		//invalidate非常重要,不然你移動一點頁面不能回復原狀
		invalidate();
	}

代碼:http://download.csdn.net/detail/baidu_nod/7731631

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved