閱讀154 返回首頁    go 阿裏雲 go 技術社區[雲棲]


創新源於模仿之三:實現左右兩個屏幕的切換


今天第三篇,模仿UCWEB的首頁,做一個可以左右滑動的雙屏。

 

其實要實現這個效果在Android中並非難事,因為官方的Launcher已經有現成的源代碼放在那兒了,就是那個Workspace.java。大家可以去https://android.git.kernel.org/ 下載。

 

而我們要做的事情就是分析它並精簡它(畢竟我們隻是打算左右滑動罷了,並不需要能創建快捷方式文件夾之類的東西,更不需要在上麵拖放圖標)。

 

public class Workspace extends ViewGroup 
 implements DropTarget, DragSource, DragScroller {

}


 

因此,不管是Drop還是Drag,統統不需要了:
 

public class Workspace extends ViewGroup {

}


 

同時,把那些個接口所要求實現的方法,以及那些與Drag/Drop相關的成員變量都去掉吧。
看看我精簡後剩下什麼成員變量:

 

    private static final int INVALID_SCREEN = -1;
   
    private int mDefaultScreen;

    private boolean mFirstLayout = true;

    private int mCurrentScreen;
    private int mNextScreen = INVALID_SCREEN;
    private Scroller mScroller;
   

    private float mLastMotionX;
    private float mLastMotionY;

    private final static int TOUCH_STATE_REST = 0;
    private final static int TOUCH_STATE_SCROLLING = 1;

    private int mTouchState = TOUCH_STATE_REST;

    private int mTouchSlop;


以上足矣。

 

然後在Eclipse中會有大量的錯誤,沒關係,刪吧。

 

addView 是用來在代碼中添加新的子view的方法,不需要,我們隻需要在layout xml中直接指定就好了。
getOpenFolder/getOpenFolders 文件夾相關的,當然不需要了。
addInCurrentScreen/addWidget 都沒什麼用處了,可以刪掉。
與Cell相關的那些也可以刪掉。

 

因為我們的代碼不能直接訪問mScrollX,所以需要換成getScrollX()。這一點是需要特別注意的。

 

看看我精簡後都剩下些什麼方法:

 

 


最後隻要不報錯,就OK了。

 

我們來分析一下幾個關鍵的方法,其一是 onTouchEvent:

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
       

        final int action = ev.getAction();
        final float x = ev.getX();

        switch (action) {
        case MotionEvent.ACTION_DOWN:

            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 (getScrollX() > 0) {
                     scrollBy(Math.max(-1*getScrollX(), deltaX), 0);                        
                    }
                } else if (deltaX > 0) {
                    final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -
                            getScrollX() - getWidth();
                    if (availableToScroll > 0) {
                     scrollBy(Math.min(availableToScroll, deltaX), 0);
                    }
                }
            }
            break;
        case MotionEvent.ACTION_UP:
     //抬起手指後,切換屏幕的處理
            if (mTouchState == TOUCH_STATE_SCROLLING) {
             snapToDestination();
            }
            mTouchState = TOUCH_STATE_REST;
            break;
        case MotionEvent.ACTION_CANCEL:
            mTouchState = TOUCH_STATE_REST;
        }

        return true;
    }


 


其二 snapToDestination和snapToScreen:

 

   private void snapToDestination() {
 //計算應該去哪個屏
        final int screenWidth = getWidth();
        final int whichScreen = (getScrollX() + (screenWidth / 2)) / screenWidth;
 //切換
        snapToScreen(whichScreen);
    }

    void snapToScreen(int whichScreen) {
        if (!mScroller.isFinished()) return;

        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
        boolean changingScreens = whichScreen != mCurrentScreen;
        
        mNextScreen = whichScreen;
        
        View focusedChild = getFocusedChild();
        if (focusedChild != null && changingScreens && focusedChild == getChildAt(mCurrentScreen)) {
            focusedChild.clearFocus();
        }

        //讓mScroller啟動滾動
        final int cx = getScrollX();
        final int newX = whichScreen * getWidth();
        final int delta = newX - cx;
        mScroller.startScroll(cx, 0, delta, 0, Math.abs(delta) * 4);
        invalidate();
    }


 

其三 computeScroll,讓Workspace滾動到合適的位置:

 

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
            postInvalidate();
        } else if (mNextScreen != INVALID_SCREEN) {
            mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
            mNextScreen = INVALID_SCREEN;            
        }
        
    }


 

基本上就是這些了,其它方法都是輔助的,很好理解。

 

其實有一個問題,我們發現UCWeb主頁下麵有三個點指示當前所處的位置,這個指示器我的想法是可以放在Workspace外麵來做,利用Workspace當前的mCurrentScreen值顯示出當前正處於哪個屏。

 

給出我的layout xml描述:

 

        <cn.sharetop.demo.ui.Workspace
         android:
         android:layout_width="fill_parent" 
         android:layout_height="640.0dip" 
         android:layout_weight="1.0"
         xmessenger:defaultScreen="0"
         >
         <include layout="@layout/screen1" />
         <include layout="@layout/screen2" />
         
        </cn.sharetop.demo.ui.Workspace>
       <TextView 
         android:layout_width="fill_parent" 
         android:layout_height="wrap_content" 
         android:textColor="#000000" 
         android:background="@android:color/transparent" 
         android:gravity="center" 
         android:text="[1]2" />


 

就這樣了。這個分頁指示器就留給你自己去發揮了。

最後更新:2017-04-02 06:51:59

  上一篇:go 創新源於模仿之一:TabActivity的美化
  下一篇:go ubuntu10.04升級到10.10