Android 自定义View 让内容跟随手指滚动

需求:

自定义一个View,当Canvas绘制的内容超过当前屏幕,希望能够通过手指触摸屏幕的方法移动画布的内容。

实现思路:

通过View的OnTouchEvent方法可以监听当前手指的位置,可以计算出滑动的距离,当前速度。通过View的scrollTo、scrollBy方法可以直接将画图移动到目标位置。如果想要手指离开后画布根据瞬时速度继续滑行的效果,可以用Scroller的fling实现。


Demo:
public class CustomView extends View{
    Paint paint = new Paint();
    OverScroller mScroller; //用于辅助View拖动或滑行
    //处理触摸的速率
    private VelocityTracker mVelocityTracker = null ; //速度追踪器
    public static final int  SNAP_VELOCITY = 600 ;  //最小的滑动速率,小于这个速率不滑行


    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mScroller=new OverScroller(getContext());
        mVelocityTracker=VelocityTracker.obtain();
    }

    /**
     * 调用Scroller的startScroll后,Scroller会根据偏移量是时间计算当前的X坐标和Y坐标,执行invalidte会让View执行draw()方法,从而调用computeScroll()方法
     * @param dx
     * @param dy
     */
    public void smoothScrollBy(int dx,int dy){
        if(mScroller.getFinalX()+dx>2160){
            dx = 2160-mScroller.getFinalX();
        }else if(mScroller.getFinalX()+dx<0){
            dx = -1*mScroller.getFinalX();
        }
        mScroller.startScroll(mScroller.getFinalX(),mScroller.getFinalY(),dx,dy,500);
        invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
    }

    /**
     * 根据瞬时速度,让画布滑行
     * @param velocityX X轴速度,有正负方向,正数画布左移
     * @param velocityY
     */
    public void fling( int velocityX, int velocityY){
        //最后两个是参数是允许的超过边界值的距离
        mScroller.fling(mScroller.getFinalX(),mScroller.getFinalY(),velocityX,velocityY,0,2160,0,0,200,200);
        invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
    }

    /**
     * 当draw()调用时此方法被调用,用来将内容滚动到Scroller的当前坐标
     */
    @Override
    public void computeScroll() {

        //先判断mScroller滚动是否完成
        if (mScroller.computeScrollOffset()) {
            //这里调用View的scrollTo()完成实际的滚动
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());

            //必须调用该方法,否则不一定能看到滚动效果
            postInvalidate();
        }
        super.computeScroll();
    }

    public void smoothScrollTo(int fx,int fy){
        int dx = fx - mScroller.getFinalX();
        int dy = fy - mScroller.getFinalY();
        smoothScrollBy(dx, dy);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomView(Context context) {
        super(context);
        init();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        paint.setColor(Color.RED);
        canvas.drawRect(0,0,1080,getMeasuredHeight(),paint);

        paint.setColor(Color.YELLOW);
        canvas.drawRect(1080,0,2160,getMeasuredHeight(),paint);

        paint.setColor(Color.BLUE);
        canvas.drawRect(2160,0,3240,getMeasuredHeight(),paint);
    }


    int lastX;
    int currentX;
    int distanceX;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mVelocityTracker.addMovement(event);
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                if(mScroller != null){
                    if(!mScroller.isFinished()){
                        mScroller.abortAnimation();
                    }
                }
                lastX= (int) event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                //计算出两次动作间的滑动距离
                currentX = (int) event.getX();
                distanceX = currentX-lastX;
                distanceX= distanceX*-1;
                lastX = currentX;
               smoothScrollBy(distanceX,0);
                break;
            case MotionEvent.ACTION_UP:
                //根据触摸位置计算每像素的移动速率。
                //A value of 1 provides pixels per millisecond, 1000 provides pixels per second, etc.
                mVelocityTracker.computeCurrentVelocity(1000);
                //计算速率
                int velocityX = (int) mVelocityTracker.getXVelocity()*(-1) ;
                int velocityY = (int)mVelocityTracker.getYVelocity();

                //计算出两次动作间的滑动距离
                currentX = (int) event.getX();
                distanceX = currentX-lastX;
                distanceX= distanceX*-1;
                lastX = currentX;
                //如果速率大于最小速率要求,执行滑行,否则拖动到位置
                if(Math.abs(velocityX)>SNAP_VELOCITY){
                    if(!mScroller.isFinished()){
                        mScroller.abortAnimation();
                    }
                    fling(velocityX,velocityY);
                }else{
                    smoothScrollBy(distanceX,0);
                }
//
                break;
        }
        return true;
    }
}







你可能感兴趣的:(android,app开发)