在学习Workspace这个类时,接触到Scroller,开始很疑惑,在ViewGroup中本身是带有
scrollTo (int x, int y);scrollBy(int x, int y);getScrollX();getScrollY ();等方法,不是可以实现滚动了吗,
为什么还要加个Scroller在里面?到底Scroller.startScroll 与scrollto有怎样的关联?
getScrollX()与Scroller.getCurrX()有什么区别?
带着这样的疑问,在网上找了很多的资料,也没怎么弄明白,后来找到一个实例,
把其中的Scroller.startScroll的方法给屏蔽掉,再看效果的时候,顿时就明白了Scroller的作用了
实例连接:http://blog.csdn.net/wangkuifeng0118/article/details/7374460是一个高仿launcher的实例,很不错
这里为了方便查看也贴一下代码:
public class MyScrollLayout extends ViewGroup{ private static final String TAG = "ScrollLayout"; private VelocityTracker mVelocityTracker; private static final int SNAP_VELOCITY = 600; private Scroller mScroller; private int mCurScreen; private int mDefaultScreen = 0; private float mLastMotionX; // private int mTouchSlop; private OnViewChangeListener mOnViewChangeListener; public MyScrollLayout(Context context) { super(context); // TODO Auto-generated constructor stub init(context); } public MyScrollLayout(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub init(context); } public MyScrollLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub init(context); } private void init(Context context) { mCurScreen = mDefaultScreen; mScroller = new Scroller(context); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { Log.d("switchView","MyScrollLayout onLayout"); // TODO Auto-generated method stub if (changed) { int childLeft = 0; final int childCount = getChildCount(); for (int i=0; i<childCount; i++) { final View childView = getChildAt(i); if (childView.getVisibility() != View.GONE) { final int childWidth = childView.getMeasuredWidth(); childView.layout(childLeft, 0, childLeft+childWidth, childView.getMeasuredHeight()); childLeft += childWidth; } } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Log.d("switchView","MyScrollLayout onMeasure"); // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int width = MeasureSpec.getSize(widthMeasureSpec); final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int count = getChildCount(); for (int i = 0; i < count; i++) { getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); } scrollTo(mCurScreen * width, 0); } public void snapToDestination() { final int screenWidth = getWidth(); final int destScreen = (getScrollX()+ screenWidth/2)/screenWidth; Log.d("switchView","MyScrollLayout snapToDestination"+getScrollX()); snapToScreen(destScreen); } public void snapToScreen(int whichScreen) { // get the valid layout page whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1)); if (getScrollX() != (whichScreen*getWidth())) { final int delta = whichScreen*getWidth()-getScrollX(); mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta)*2); Log.d("switchView","MyScrollLayout snapToScreen delta="+delta); mCurScreen = whichScreen; invalidate(); // Redraw the layout if (mOnViewChangeListener != null) { mOnViewChangeListener.OnViewChange(mCurScreen); } } } @Override public void computeScroll() { Log.d("switchView","MyScrollLayout computeScroll"+getScrollX()); // TODO Auto-generated method stub Log.d("switchView","MyScrollLayout computeScroll mScroller.getCurrX()"+mScroller.getCurrX()); if (mScroller.computeScrollOffset()) { Log.d("switchView","MyScrollLayout computeScroll mScroller.getCurrX()"+mScroller.getCurrX()); scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub final int action = event.getAction(); final float x = event.getX(); final float y = event.getY(); switch (action) { case MotionEvent.ACTION_DOWN: Log.i("", "onTouchEvent ACTION_DOWN"); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(event); } if (!mScroller.isFinished()){ mScroller.abortAnimation(); } mLastMotionX = x; break; case MotionEvent.ACTION_MOVE: int deltaX = (int)(mLastMotionX - x); Log.d("switchView","MyScrollLayout onTouchEvent"+mLastMotionX+";x="+x); if (IsCanMove(deltaX)) { if (mVelocityTracker != null) { mVelocityTracker.addMovement(event); } mLastMotionX = x; scrollBy(deltaX, 0); } break; case MotionEvent.ACTION_UP: int velocityX = 0; if (mVelocityTracker != null) { mVelocityTracker.addMovement(event); mVelocityTracker.computeCurrentVelocity(1000); velocityX = (int) mVelocityTracker.getXVelocity(); } if (velocityX > SNAP_VELOCITY && mCurScreen > 0) { // Fling enough to move left Log.e(TAG, "snap left"); snapToScreen(mCurScreen - 1); } else if (velocityX < -SNAP_VELOCITY && mCurScreen < getChildCount() - 1) { // Fling enough to move right Log.e(TAG, "snap right"); snapToScreen(mCurScreen + 1); } else { snapToDestination(); } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } // mTouchState = TOUCH_STATE_REST; break; } return true; } private boolean IsCanMove(int deltaX) { if (getScrollX() <= 0 && deltaX < 0 ){ return false; } if (getScrollX() >= (getChildCount() - 1) * getWidth() && deltaX > 0){ return false; } return true; } public void SetOnViewChangeListener(OnViewChangeListener listener) { mOnViewChangeListener = listener; } }
下面的方法是在MotionEvent.ACTION_UP后调用的,屏蔽掉此方法后,当手指停止滑动后,屏幕就停止滑动,
不会自动跳转到下一屏,也就是说在手指离开屏幕后,接下来的滚动都是由Scroller来完成的,而之前的滚动
是由ViewGroup的scrollBy完成的
mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta)*2);
此时工作还没完,Scroller和ViewGroup是两个独立的个体,Scroller滚动的时候,
View不会跟着就滚动,需要手动加代码让它滚动:
public void computeScroll() { // TODO Auto-generated method stub if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } }
其中scrollTo就是让View跟着Scroller滚动,
其中mScroller.computeScrollOffset()不仅是判断滚动是否结束,
还在里面对Scroller.mCurrX进行了赋值,也就是在滚动的过程中,实时的更新CurrX的坐标,
所以scrollTo(mScroller.getCurrX(), mScroller.getCurrY());就能跟着Scroller滚动了
看源码:
public boolean computeScrollOffset() { if (mFinished) { return false; } int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); if (timePassed < mDuration) { switch (mMode) { case SCROLL_MODE: float x = (float)timePassed * mDurationReciprocal; if (mInterpolator == null) x = viscousFluid(x); else x = mInterpolator.getInterpolation(x); mCurrX = mStartX + Math.round(x * mDeltaX); mCurrY = mStartY + Math.round(x * mDeltaY); break; case FLING_MODE: float timePassedSeconds = timePassed / 1000.0f; float distance = (mVelocity * timePassedSeconds) - (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f); mCurrX = mStartX + Math.round(distance * mCoeffX); // Pin to mMinX <= mCurrX <= mMaxX mCurrX = Math.min(mCurrX, mMaxX); mCurrX = Math.max(mCurrX, mMinX); mCurrY = mStartY + Math.round(distance * mCoeffY); // Pin to mMinY <= mCurrY <= mMaxY mCurrY = Math.min(mCurrY, mMaxY); mCurrY = Math.max(mCurrY, mMinY); if (mCurrX == mFinalX && mCurrY == mFinalY) { mFinished = true; } break; } } else { mCurrX = mFinalX; mCurrY = mFinalY; mFinished = true; } return true; }
而当View的坐标发生改变时都会调用computeScroll()方法,从而实现了整个滑动的动态控制。