原文链接:http://blog.csdn.net/txj8612/article/details/8838814。
在学习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()方法,从而实现了整个滑动的动态控制。