Android自定义View之使用Path绘制手势轨迹和水波效果

来源:互联网 发布:mac程序文件夹在哪里 编辑:程序博客网 时间:2024/06/05 01:58

先看下效果图:
path.gif

ripple.gif

绘制轨迹

绘制手指的轨迹主要是拦截View的onTouchEvent()方法,并根据手指的轨迹绘制path。path中有两种可以实现的方法

1、Path.lineTo(x,y)方法
public class MovePathView extends View {    private Path mPath;    private Paint mPaint;    //手指按下的位置    private float startX,startY;    public MovePathView(Context context) {        super(context);        init();    }    //初始化    private void init() {        mPaint = new Paint();        mPath = new Path();        mPaint.setColor(Color.BLUE);        mPaint.setAntiAlias(true);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setStrokeWidth(4);    }    public MovePathView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public MovePathView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                startX = event.getX();                startY = event.getY();                //设置原点                mPath.moveTo(startX,startY);                break;            case MotionEvent.ACTION_UP:                break;            case MotionEvent.ACTION_MOVE:                float currX = event.getX();                float currY = event.getY();                //连线                mPath.lineTo(currX,currY);                //刷新view                invalidate();                break;        }        //返回true,消费事件        return true;    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawPath(mPath,mPaint);    }    //对外提供的方法,重新绘制    public void reset(){        mPath.reset();        invalidate();    }}

这里面需要知道的应该就3个点:
- View的坐标系
- View的事件分发
- Path的moveTo(),lineTo()方法

2、使用Path.quadTo()绘制曲线
public class MoveQuatoView extends View {    private Paint mPaint;    private Path mPath;    //上个位置    private float mPreX,mPreY;    //结束位置    private float endY,endX;    public MoveQuatoView(Context context) {        super(context);        init();    }    public MoveQuatoView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public MoveQuatoView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    //初始化    private void init() {        mPath = new Path();        mPaint = new Paint();        mPaint.setColor(Color.RED);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setStrokeWidth(5);        mPaint.setAntiAlias(true);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawPath(mPath,mPaint);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                mPath.moveTo(event.getX(), event.getY());                mPreX = event.getX();                mPreY = event.getY();                break;            case MotionEvent.ACTION_MOVE:                endX = (mPreX + event.getX()) / 2;                endY = (mPreY + event.getY()) / 2;                mPath.quadTo(mPreX, mPreY, endX, endY);                mPreX = event.getX();                mPreY = event.getY();                invalidate();            break;        }        return true;    }}

上面一段代码为了取得平滑的效果,所以endX和endY都只取了直线的中间部分。

水波纹效果

水波纹主要用到了Path.rQuadTo()方法。
rQuadTo()也是绘制曲线的一个方法。
image.png

 @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        Paint paint = new Paint();        paint.setColor(Color.RED);        paint.setStrokeWidth(5);        paint.setStyle(Paint.Style.STROKE);        paint.setAntiAlias(true);        Path path = new Path();        path.moveTo(100,300);        /**           rQuadTo(float dx1, float dy1, float dx2, float dy2)             dx1:控制点X坐标,表示相对上一个终点X坐标的位移坐标,可为负值,正值表示相加,负值表示相减;           dy1:控制点Y坐标,相对上一个终点Y坐标的位移坐标。同样可为负值,正值表示相加,负值表示相减;           dx2:终点X坐标,同样是一个相对坐标,相对上一个终点X坐标的位移值,可为负值,正值表示相加,负值表示相减;           dy2:终点Y坐标,同样是一个相对,相对上一个终点Y坐标的位移值。可为负值,正值表示相加,负值表示相减;         */        path.rQuadTo(100,-100,200,0);        path.rQuadTo(100,100,200,0);        canvas.drawPath(path,paint);    }

上面代码总共有两个rQuadTo()方法。
第一个path.rQuadTo(100,-100,200,0);
起始点:(100,300)
控制点坐标:(200,200),X:200=100+100,Y: 200 = 300-100
终点坐标: (300,300), X :300=100+200,Y:300 = 300+0
效果是:![image2.png](http://upload-images.jianshu.io/upload_images/2729169-8a82e6e36cd5cf8b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
第二个
path.rQuadTo(100,100,200,0);`
此时的起始点坐标也就是第一个的终点坐标,所以
起始点坐标:(300,300)
控制点坐标:(400,400),X: 400 = 300+100,Y:400 = 300+100
终点坐标: (500,300),X: 500 = 300+200,Y:300 = 300+0
同理,如果有第三个path.rQuadTo,那么第三个的起始点也就是上一个的终点(500,300)

搞清楚了path.rQuadTo()方法的用法就可以去实现水波纹的效果了。

public class RippleView extends View {    private Paint mPaint;    private Path mPath;    //波纹的宽度    private int mItemWaveLength = 1000;    //波纹每次移动的距离    private int dx;    public RippleView(Context context) {        super(context);        init();    }    public RippleView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public RippleView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    //初始化    private void init(){        mPath = new Path();        mPaint = new Paint();        mPaint.setColor(Color.RED);        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);        mPaint.setStrokeWidth(5);        mPaint.setAntiAlias(true);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //移动后,重置mPath,将之前路径清空        mPath.reset();        //距离顶部的高度        int originY = 600;        //波纹宽度的一般        int halfWaveLen = mItemWaveLength/2;        //随着刷新,每次移动dx距离        mPath.moveTo(-mItemWaveLength+dx,originY);        //for循环当前屏幕中所有的波纹        for (int i = -mItemWaveLength;i<=getWidth()+mItemWaveLength;i+=mItemWaveLength){            mPath.rQuadTo(halfWaveLen/2,-100,halfWaveLen,0);            mPath.rQuadTo(halfWaveLen/2,100,halfWaveLen,0);        }        mPath.lineTo(getWidth(),getHeight());        mPath.lineTo(0,getHeight());        mPath.close();        canvas.drawPath(mPath,mPaint);    }    /**     * 动画的目的是让波纹移动起来     * 利用调用在path.moveTo的时候,将起始点向右移动即可实现移动,     * 而且只要我们移动一个波长的长度,波纹就会重合,就可以实现无限循环了     */    public void startAnim(){        //动画移动的距离 0~mItemWaveLength        ValueAnimator animator = ValueAnimator.ofInt(0,mItemWaveLength);        //时间        animator.setDuration(2000);        //重复次数,这里是无限次        animator.setRepeatCount(ValueAnimator.INFINITE);        animator.setInterpolator(new LinearInterpolator());        //动画刷新监听        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                //每次移动的距离                dx = (int)animation.getAnimatedValue();                //刷新View                postInvalidate();            }        });        animator.start();    }}

这样就实现了一个水波纹的效果了。

本文参考:
自定义控件三部曲之绘图篇(六)——Path之贝赛尔曲线和手势轨迹、水波纹效果

阅读全文
0 0
原创粉丝点击