Scroller 实例讲解

在学习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()方法,从而实现了整个滑动的动态控制。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值