• 代码解说Android Scroller、VelocityTracker


    在编写自己定义滑动控件时经常会用到Android触摸机制和Scroller及VelocityTracker。Android Touch系统简单介绍(二):实例具体解释onInterceptTouchEvent与onTouchEvent的调用过程对Android触摸机制须要用到的函数进行了具体的解释。本文主要介绍两个重要的类:Scroller及VelocityTracker。利用上述知识,最后给出了一个自己定义滑动控件的demo,该demo类似于ImageGallery

    ImageGallery通常是用GridView来实现的,能够左右滑动。本样例实现的控件直接继承一个ViewGroup,对其回调函数如 onTouchEvent、onInterceptTouchEvent、computeScroll等进行重载。弄懂该代码。对Android touch的认识将会更深一层。

    VelocityTracker:用于对触摸点的速度跟踪,方便获取触摸点的速度。
    使用方法:一般在onTouchEvent事件中被调用。先在down事件中获取一个VecolityTracker对象,然后在move或up事件中获取速度,调用流程可例如以下列所看到的:

    VelocityTracker vTracker = null;
    @Override  
    public boolean onTouchEvent(MotionEvent event){  
    	int action = event.getAction();  
    	switch(action){  
    	case MotionEvent.ACTION_DOWN:  
    		if(vTracker == null){  
    			vTracker = VelocityTracker.obtain();  
    		}else{  
    			vTracker.clear();  
    		}  
    		vTracker.addMovement(event);  
    		break;  
    	case MotionEvent.ACTION_MOVE:  
    		vTracker.addMovement(event);  
    		//设置单位,1000 表示每秒多少像素(pix/second),1代表每微秒多少像素(pix/millisecond)。 
    		vTracker.computeCurrentVelocity(1000);  
    		//从左向右划返回正数,从右向左划返回负数
    		System.out.println("the x velocity is "+vTracker.getXVelocity());  
    		//从上往下划返回正数,从下往上划返回负数
    		System.out.println("the y velocity is "+vTracker.getYVelocity());  
    		break;  
    	case MotionEvent.ACTION_UP:  
    	case MotionEvent.ACTION_CANCEL:  
    		vTracker.recycle();  
    		break;  
    	}  
    	return true;  
    }  


    Scroller:用于跟踪控件滑动的轨迹。此类不会移动控件,须要你在View的一个回调函数computerScroll()中使用Scroller对象还获取滑动的数据来控制某个View。

      /**
     * Called by a parent to request that a child update its values for mScrollX
     * and mScrollY if necessary. This will typically be done if the child is
     * animating a scroll using a {@link android.widget.Scroller Scroller}
     * object.
     */
    public void computeScroll()
    {
    }
    parentView在绘制式。会调用dispatchDraw(Canvas canvas),该函数会调用ViewGroup中的每一个子view的boolean draw(Canvas canvas, ViewGroup parent, long drawingTime),用户绘制View,此函数在绘制View的过程中会调用computeScroll()
    以下给出一段代码:
    @Override
    public void computeScroll() {	
    	// TODO Auto-generated method stub
    	Log.e(TAG, "computeScroll");
    	if (mScroller.computeScrollOffset()) { //or !mScroller.isFinished()
    		Log.e(TAG, mScroller.getCurrX() + "======" + mScroller.getCurrY());
    		scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
    		Log.e(TAG, "### getleft is " + getLeft() + " ### getRight is " + getRight());
    		postInvalidate();
    	}
    	else
    		Log.i(TAG, "have done the scoller -----");
    }
    这段代码在滑动view之前先调用mScroller.computeScrollOffset()来推断滑动动画是否已结束。computerScrollerOffset()的源码例如以下:

    /**
     * Call this when you want to know the new location.  If it returns true,
     * the animation is not yet finished.
     */ 
    public boolean computeScrollOffset() {
    	if (mFinished) {
    		return false;
    	}
    	
    	//滑动已经持续的时间
    	int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    	//若在规定时间还未用完,则继续设置新的滑动位置mCurrX和mCurry
    	if (timePassed < mDuration) {
    		switch (mMode) {
    		case SCROLL_MODE:
    			float x = 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:
    			final float t = (float) timePassed / mDuration;
    			final int index = (int) (NB_SAMPLES * t);
    			float distanceCoef = 1.f;
    			float velocityCoef = 0.f;
    			if (index < NB_SAMPLES) {
    				final float t_inf = (float) index / NB_SAMPLES;
    				final float t_sup = (float) (index + 1) / NB_SAMPLES;
    				final float d_inf = SPLINE_POSITION[index];
    				final float d_sup = SPLINE_POSITION[index + 1];
    				velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
    				distanceCoef = d_inf + (t - t_inf) * velocityCoef;
    			}
    
    			mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
    			
    			mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
    			// Pin to mMinX <= mCurrX <= mMaxX
    			mCurrX = Math.min(mCurrX, mMaxX);
    			mCurrX = Math.max(mCurrX, mMinX);
    			
    			mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
    			// 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;
    }
    ViewGroup.computeScroll()被调用时机:
    当我们运行ontouch或invalidate()或postInvalidate()都会导致这种方法的运行。

    我们在开发控件时。常会有这种需求:当单机某个button时。某个图片会在规定的时间内滑出窗体。而不是一下子进入窗体。实现这个功能能够使用Scroller来实现。
    以下给出一段代码,该代码控制下一个界面在3秒时间内缓慢进入的效果。

    public void moveToRightSide(){
    	if (curScreen <= 0) {
    		return;
    	}
    	curScreen-- ;
    	Log.i(TAG, "----moveToRightSide---- curScreen " + curScreen);
    	mScroller.startScroll((curScreen + 1) * getWidth(), 0, -getWidth(), 0, 3000);
    	scrollTo(curScreen * getWidth(), 0);
    	invalidate();
    }
    上述代码用到了一个函数:void android.widget.Scroller.startScroll(int startX, int startY, int dx, int dy, int duration)
    当startScroll运行过程中即在duration时间内,computeScrollOffset  方法会一直返回true,但当动画运行完毕后会返回返加false.
    这个函数的源代码例如以下所看到的,主要用于设置滑动參数

    /**
     * Start scrolling by providing a starting point, the distance to travel,
     * and the duration of the scroll.
     * 
     * @param startX Starting horizontal scroll offset in pixels. Positive
     *        numbers will scroll the content to the left.
     * @param startY Starting vertical scroll offset in pixels. Positive numbers
     *        will scroll the content up.
     * @param dx Horizontal distance to travel. Positive numbers will scroll the
     *        content to the left.
     * @param dy Vertical distance to travel. Positive numbers will scroll the
     *        content up.
     * @param duration Duration of the scroll in milliseconds.
     */
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    	mMode = SCROLL_MODE;
    	mFinished = false;
    	mDuration = duration;
    	mStartTime = AnimationUtils.currentAnimationTimeMillis();
    	mStartX = startX;
    	mStartY = startY;
    	mFinalX = startX + dx;
    	mFinalY = startY + dy;
    	mDeltaX = dx;
    	mDeltaY = dy;
    	mDurationReciprocal = 1.0f / (float) mDuration;
    }
    
    invalidate()会使得视图重绘,导致parent调用了dispatchDraw(Canvas canvas),然后递归调用child View的draw()函数。该函数又会调用我们定义的computeScroll(), 而这个函数又会调用mScroller.computeScrollOffset()推断动画是否结束。若没结束则继续重绘直到直到startScroll中设置的时间耗尽mScroller.computeScrollOffset()返回false才停下来。

    附上完整的实例代码:

    自己定义Android可滑动控件源代码

    执行效果图例如以下,滑动屏幕会显示不同的图片。







  • 相关阅读:
    禁止浏览器缩放功能。
    布局
    设置页面大小
    常用英语
    iOS沙盒路径的简单介绍
    关于 pragma使用
    3Dtouch API Peek and Pop
    3Dtouch API Home Screen Quick Actions
    创建一个.framework静态库
    新学期和学生一起尝试使用博客
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/6714587.html
Copyright © 2020-2023  润新知