创新源于模仿之三:实现左右两个屏幕的切换

来源:互联网 发布:网易邮箱数据疑似泄露 编辑:程序博客网 时间:2024/06/06 00:07
今天第三篇,模仿UCWEB的首页,做一个可以左右滑动的双屏。

 

其实要实现这个效果在Android中并非难事,因为官方的Launcher已经有现成的源代码放在那儿了,就是那个Workspace.java。大家可以去http://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:id="@id/friends_switcher"         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" />


就这样了。这个分页指示器就留给你自己去发挥了。