Android功能總結:仿照Launcher的Workspace實現左右滑動切換

來源:互聯網
上載者:User

 

 

對於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源碼:

package com.yao_guet.test;<br />import Android.content.Context;<br />import android.graphics.Canvas;<br />import android.util.AttributeSet;<br />import android.util.Log;<br />import android.view.MotionEvent;<br />import android.view.VelocityTracker;<br />import android.view.View;<br />import android.view.ViewConfiguration;<br />import android.view.ViewGroup;<br />import android.widget.Scroller;<br />/**<br /> * 仿Launcher中的WorkSapce,可以左右滑動切換畫面的類<br /> * @author Yao.GUET<br /> * blog: http://blog.csdn.NET/Yao_GUET<br /> * date: 2011-05-04<br /> */<br />public class ScrollLayout extends ViewGroup {<br /> private static final String TAG = "ScrollLayout";<br /> private Scroller mScroller;<br /> private VelocityTracker mVelocityTracker; </p><p> private int mCurScreen;<br /> private int mDefaultScreen = 0; </p><p> private static final int TOUCH_STATE_REST = 0;<br /> private static final int TOUCH_STATE_SCROLLING = 1; </p><p> private static final int SNAP_VELOCITY = 600; </p><p> private int mTouchState = TOUCH_STATE_REST;<br /> private int mTouchSlop;<br /> private float mLastMotionX;<br /> private float mLastMotionY;<br /> public ScrollLayout(Context context, AttributeSet attrs) {<br /> this(context, attrs, 0);<br /> // TODO Auto-generated constructor stub<br /> }<br /> public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {<br /> super(context, attrs, defStyle);<br /> // TODO Auto-generated constructor stub<br /> mScroller = new Scroller(context); </p><p> mCurScreen = mDefaultScreen;<br /> mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();<br /> }<br /> @Override<br /> protected void onLayout(boolean changed, int l, int t, int r, int b) {<br /> // TODO Auto-generated method stub<br /> if (changed) {<br /> int childLeft = 0;<br /> final int childCount = getChildCount(); </p><p> for (int i=0; i<childCount; i++) {<br /> final View childView = getChildAt(i);<br /> if (childView.getVisibility() != View.GONE) {<br /> final int childWidth = childView.getMeasuredWidth();<br /> childView.layout(childLeft, 0,<br /> childLeft+childWidth, childView.getMeasuredHeight());<br /> childLeft += childWidth;<br /> }<br /> }<br /> }<br /> }<br /> @Override<br /> protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {<br /> Log.e(TAG, "onMeasure");<br /> super.onMeasure(widthMeasureSpec, heightMeasureSpec); </p><p> final int width = MeasureSpec.getSize(widthMeasureSpec);<br /> final int widthMode = MeasureSpec.getMode(widthMeasureSpec);<br /> if (widthMode != MeasureSpec.EXACTLY) {<br /> throw new IllegalStateException("ScrollLayout only canmCurScreen run at EXACTLY mode!");<br /> } </p><p> final int heightMode = MeasureSpec.getMode(heightMeasureSpec);<br /> if (heightMode != MeasureSpec.EXACTLY) {<br /> throw new IllegalStateException("ScrollLayout only can run at EXACTLY mode!");<br /> } </p><p> // The children are given the same width and height as the scrollLayout<br /> final int count = getChildCount();<br /> for (int i = 0; i < count; i++) {<br /> getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);<br /> }<br /> // Log.e(TAG, "moving to screen "+mCurScreen);<br /> scrollTo(mCurScreen * width, 0);<br /> } </p><p> /**<br /> * According to the position of current layout<br /> * scroll to the destination page.<br /> */<br /> public void snapToDestination() {<br /> final int screenWidth = getWidth();<br /> final int destScreen = (getScrollX()+ screenWidth/2)/screenWidth;<br /> snapToScreen(destScreen);<br /> } </p><p> public void snapToScreen(int whichScreen) {<br /> // get the valid layout page<br /> whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1));<br /> if (getScrollX() != (whichScreen*getWidth())) { </p><p> final int delta = whichScreen*getWidth()-getScrollX();<br /> mScroller.startScroll(getScrollX(), 0,<br /> delta, 0, Math.abs(delta)*2);<br /> mCurScreen = whichScreen;<br /> invalidate(); // Redraw the layout<br /> }<br /> } </p><p> public void setToScreen(int whichScreen) {<br /> whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1));<br /> mCurScreen = whichScreen;<br /> scrollTo(whichScreen*getWidth(), 0);<br /> } </p><p> public int getCurScreen() {<br /> return mCurScreen;<br /> } </p><p> @Override<br /> public void computeScroll() {<br /> // TODO Auto-generated method stub<br /> if (mScroller.computeScrollOffset()) {<br /> scrollTo(mScroller.getCurrX(), mScroller.getCurrY());<br /> postInvalidate();<br /> }<br /> }<br /> @Override<br /> public boolean onTouchEvent(MotionEvent event) {<br /> // TODO Auto-generated method stub </p><p> if (mVelocityTracker == null) {<br /> mVelocityTracker = VelocityTracker.obtain();<br /> }<br /> mVelocityTracker.addMovement(event); </p><p> final int action = event.getAction();<br /> final float x = event.getX();<br /> final float y = event.getY(); </p><p> switch (action) {<br /> case MotionEvent.ACTION_DOWN:<br /> Log.e(TAG, "event down!");<br /> if (!mScroller.isFinished()){<br /> mScroller.abortAnimation();<br /> }<br /> mLastMotionX = x;<br /> break; </p><p> case MotionEvent.ACTION_MOVE:<br /> int deltaX = (int)(mLastMotionX - x);<br /> mLastMotionX = x; </p><p> scrollBy(deltaX, 0);<br /> break; </p><p> case MotionEvent.ACTION_UP:<br /> Log.e(TAG, "event : up");<br /> // if (mTouchState == TOUCH_STATE_SCROLLING) {<br /> final VelocityTracker velocityTracker = mVelocityTracker;<br /> velocityTracker.computeCurrentVelocity(1000);<br /> int velocityX = (int) velocityTracker.getXVelocity();<br /> Log.e(TAG, "velocityX:"+velocityX); </p><p> if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {<br /> // Fling enough to move left<br /> Log.e(TAG, "snap left");<br /> snapToScreen(mCurScreen - 1);<br /> } else if (velocityX < -SNAP_VELOCITY<br /> && mCurScreen < getChildCount() - 1) {<br /> // Fling enough to move right<br /> Log.e(TAG, "snap right");<br /> snapToScreen(mCurScreen + 1);<br /> } else {<br /> snapToDestination();<br /> }<br /> if (mVelocityTracker != null) {<br /> mVelocityTracker.recycle();<br /> mVelocityTracker = null;<br /> }<br /> // }<br /> mTouchState = TOUCH_STATE_REST;<br /> break;<br /> case MotionEvent.ACTION_CANCEL:<br /> mTouchState = TOUCH_STATE_REST;<br /> break;<br /> } </p><p> return true;<br /> }<br /> @Override<br /> public boolean onInterceptTouchEvent(MotionEvent ev) {<br /> // TODO Auto-generated method stub<br /> Log.e(TAG, "onInterceptTouchEvent-slop:"+mTouchSlop); </p><p> final int action = ev.getAction();<br /> if ((action == MotionEvent.ACTION_MOVE) &&<br /> (mTouchState != TOUCH_STATE_REST)) {<br /> return true;<br /> } </p><p> final float x = ev.getX();<br /> final float y = ev.getY(); </p><p> switch (action) {<br /> case MotionEvent.ACTION_MOVE:<br /> final int xDiff = (int)Math.abs(mLastMotionX-x);<br /> if (xDiff>mTouchSlop) {<br /> mTouchState = TOUCH_STATE_SCROLLING; </p><p> }<br /> break; </p><p> case MotionEvent.ACTION_DOWN:<br /> mLastMotionX = x;<br /> mLastMotionY = y;<br /> mTouchState = mScroller.isFinished()? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;<br /> break; </p><p> case MotionEvent.ACTION_CANCEL:<br /> case MotionEvent.ACTION_UP:<br /> mTouchState = TOUCH_STATE_REST;<br /> break;<br /> } </p><p> return mTouchState != TOUCH_STATE_REST;<br /> } </p><p>}  

 

 

測試程式布局:

 

<?xml version="1.0" encoding="utf-8"?><br /><com.yao_guet.test.ScrollLayout<br /> xmlns:android="http://schemas.android.com/apk/res/android"<br /> android:id="@+id/ScrollLayoutTest"<br /> android:layout_width="fill_parent"<br /> android:layout_height="fill_parent"><br /><LinearLayout<br /> android:background="#FF00"<br /> android:layout_width="fill_parent"<br /> android:layout_height="fill_parent"></LinearLayout> </p><p><FrameLayout<br /> android:background="#F0F0"<br /> android:layout_width="fill_parent"<br /> android:layout_height="fill_parent"></FrameLayout> </p><p><FrameLayout<br /> android:background="#F00F"<br /> android:layout_width="fill_parent"<br /> android:layout_height="fill_parent"><br /> </FrameLayout> </p><p><LinearLayout<br /> android:background="#FF00"<br /> android:layout_width="fill_parent"<br /> android:layout_height="fill_parent"><br /> <Button<br /> android:layout_width="wrap_content"<br /> android:layout_height="wrap_content"<br /> android:text="Button1" /><br /> </LinearLayout><br /><LinearLayout<br /> android:layout_width="wrap_content"<br /> android:layout_height="wrap_content"><br /> <Button<br /> android:layout_width="wrap_content"<br /> android:layout_height="wrap_content"<br /> android:text="Button2" /><br /> </LinearLayout><br /></com.yao_guet.test.ScrollLayout>  

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.