实例演示自定义View和Scroller的使用

在之前的文章中,已经介绍过View的坐标系,滑动等相关内容。今天结合一个具体的例子来演示一下。
首先介绍一下Scroller。

Scroller

Scroller是一个滑动的辅助类,它主要包含以下内容

  1. startScroll(int startX, int startY, int dx, int dy, int duration)
    startX: 滑动开始的X位置
    dx: 将要滑动的距离
    duration:在多长时间内完成滑动

  2. getScrollX(),getScrollY()
    获取滑动距离

需要注意的是,在Scroll相关的方法中,包括scrollTo,scrollBy,getScrollX(),startScroll,他们的参数是有方向的,与View的坐标系不同,向右,向下为负值,反之为正值。

举个例子:
首先通过scrollTo(100,50)将View的内容滑动,View的位置会向左,上移动 100,50 个像素。此时,通过getScrollX/Y()获取到的值,就是 100 , 50

然后通过scrollBy(-20, -10)再移动一下View,此时View会向右,下移动 20,10 个像素,然后通过getScrollX/Y()获取到的值为100-20=80,50-10=40,View的内容位于初始位置的左上方。

简单的总结一下:

getScrollX/Y()得到的是当前View的内容与View的初始位置之间的距离,右下为负,反之为正。
startScroll(int startX, int startY, int dx, int dy, int duration):
startX/Y表示滑动开始的位置(通常由getScrollX/Y()得到)
dX/Y表示将要滑动的距离,右下为负,反之为正。
稍后会在具体的例子中演示。

  1. invalidate();
    invalidate();是View中的方法,会调用draw,进行View重绘,跟在startScroll方法之后。startScroll实际上只是告诉View怎样滑动,invalidate()之后才会开始滑动。

  2. 重写computeScroll()
    这个方法才是实现弹性滑动的关键。弹性滑动,可以理解为平滑的移动,我们通过scrollTo/By,实现的滑动是一下就完成的,弹性滑动是缓慢平滑的移动。computeScroll()是怎样实现弹性滑动的呢?
    看一下代码就明白了:

    @Override
    public void computeScroll() {
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

computeScroll是在draw方法中调用的,startScroll之后的invalidate会调用draw方法,draw又调用了computeScroll,if里的条件mScroller.computeScrollOffset()是判断滑动是否完成,如果没有完成,就会调用scrollTo,将view滑动到当前位置,然后再postInvalidate继续调用draw方法,最终将View一点一点移动到目标位置。

实例演示

我们要实现的是一个很简单的例子,自定义一个圆形,当手指按下的时候,圆形移动到手指的位置,手指移动的时候,圆形会跟随手指移动,抬起手指的时候,圆形慢慢的回复到初始位置。
代码如下:

public class FollowFingerView extends View{

    //绘制圆形
    private Paint mPaint;

    //绘制背景
    private Paint mBackGroundPaint;
    //View的宽和高
    private int mWidth;
    private int mHeight;
    //定义Scroller
    private Scroller mScroller;
    //存储上次View的位置参数
    private int mLastX;
    private int mLastY;
    
    public FollowFingerView(Context context) {
        this(context,null);
        // TODO Auto-generated constructor stub
    }
    public FollowFingerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
        // TODO Auto-generated constructor stub
    }
    private void init(){
        mPaint = new Paint();
//设置圆形颜色
        mPaint.setColor(0x22ff0000);
        mBackGroundPaint=new Paint();
//设置背景颜色
        mBackGroundPaint.setColor(0xfff8efe0);
        mScroller = new Scroller(getContext());
//设置默认宽高,wrap_content时使用
        mWidth = 400;
        mHeight = 400;
    }
@Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
//绘制背景
        canvas.drawPaint(mBackGroundPaint);
    //绘制半径为30像素的圆形
        int radius = 30;
        canvas.drawCircle(30,  30, radius, mPaint);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
//处理wrap_content失效
        setMeasuredDimension(measureWidth(widthMode,width), measureHeight(heightMode,height));
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
  //获取触发事件时,手指的触碰位置
        int x = (int) event.getX();
        int y = (int) event.getY();
        
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            //手指按下时,移动View到触碰位置
            scrollTo(-x, -y);
            break;
        case MotionEvent.ACTION_MOVE:
//事件发生时手指的位置与上一个事件结束时手指的位置,之间的距离
//注意方向,可能这样写更容易理解:
//int dx = -(x - mLastX);
//int dy = -(y - mLastY);
            int dy = mLastY - y;
            int dx = mLastX - x;
            int dy = mLastY - y;
            //跟随手指移动
            scrollBy(dx, dy);
            break;
            
        case MotionEvent.ACTION_UP:
//手指抬起的时候,开始返回初始位置
            mScroller.startScroll(getScrollX(), getScrollY(), -getScrollX(), -getScrollY(), 500);
            Log.d("Follow", "getScrollX is " + getScrollX());
            invalidate();
            break;

        default:
            break;
        }
//记录事件结束时手指的位置
        mLastX = x;
        mLastY = y;
    //该View不是clickable的,返回值默认为false,应该手动改为true,否则不能消费事件,只能执行down
    //return super.onTouchEvent(event);
        return true;
    }

    @Override
    public void computeScroll() {
        // TODO Auto-generated method stub
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

    private int measureWidth(int widthMode,int widthSize){
        switch (widthMode) {
        
        case MeasureSpec.EXACTLY:
            mWidth=widthSize;
            //Log.d("ViewConstructor", "mode is exactly");
            break;
        
        case MeasureSpec.UNSPECIFIED:
        case MeasureSpec.AT_MOST:
            break;
        default:
            break;
        }
        return mWidth;
    }
    
    private int measureHeight(int heightMode,int heightSize){
        
        switch (heightMode) {
        
        case MeasureSpec.EXACTLY:
            mHeight=heightSize;
            break;
            
        case MeasureSpec.UNSPECIFIED:
        case MeasureSpec.AT_MOST:
            break;
        default:
            break;
        }
        return mHeight;
    }
}

你可能感兴趣的:(实例演示自定义View和Scroller的使用)