美文网首页
实例演示自定义View和Scroller的使用

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

作者: Cris_Ma | 来源:发表于2017-05-04 15:14 被阅读0次

    在之前的文章中,已经介绍过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的使用

          本文链接:https://www.haomeiwen.com/subject/rvrnzttx.html