Android自定义控件入门到精通--Path路径

《Android自定义控件入门到精通》文章索引 ☞ https://blog.csdn.net/Jhone_csdn/article/details/118146683

《Android自定义控件入门到精通》所有源码 ☞ https://gitee.com/zengjiangwen/Code

Path

在几何图形的绘制中,我们发现并没有画三角形等多边形的函数,以及如何画任意形状的图形?

Path用来描述一个路径,通过Path可以构建各种形状

三角形

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);

        Path path=new Path();
        path.moveTo(120,20);
        path.lineTo(160,80);
        path.lineTo(80,80);
        path.lineTo(120,20);
        canvas.drawPath(path,mPaint);

        mPaint.setStyle(Paint.Style.STROKE);

        Path path2=new Path();
        path2.moveTo(240,20);
        path2.lineTo(280,80);
        path2.lineTo(200,80);
        path2.close();
        canvas.drawPath(path2,mPaint);
    }

在这里插入图片描述

跟我们画画一样,通过path.moveTo(x,y)把笔头移动到一个点开始下笔,通过lineTo(x,y),在纸上画线段,就可以画出自己想要的图形啦,是不是很形象。

可以看到path是通过lineTo(x,y)回到原点闭合路径,path2是通过path2.close()闭合路径,所以起点和终点的闭合可以有这两种方式。

开头我们说过,Path是用来表示路径的,任何图形,都有它的路径,所以,通过Path,我们可以画任意图形

几何图形

addOval(RectF oval,Direction dir)//椭圆
addRect(RectF rect,Direction dir)//矩形
addCircle(float x, float y, float radius,Direction dir)//圆
addArc(RectF oval, float startAngle, float sweepAngle)//弧
addRoundRect(RectF rect, float[] radii, Direction dir)//圆角矩形,radii的size=8,为各个圆角的两个半径
    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.addOval(new RectF(20, 20, 40, 80), Path.Direction.CW);
        path.addRect(new RectF(80, 20, 120, 80), Path.Direction.CCW);
        path.addCircle(180, 60, 40, Path.Direction.CW);
        path.addArc(new RectF(20, 160, 40, 240), 30, 160);
        path.addRoundRect(new RectF(100, 160, 140, 240), 6, 6, Path.Direction.CCW);
        canvas.drawPath(path, mPaint);
    }

在这里插入图片描述

可以看到,除了path.addArc没有Direction参数外,其它方法都有

  • Direction.CW : clockwise 顺时针

  • Direction.CCW :counter-clockwise 逆时针

在上面的代码中,我们画椭圆和圆是顺时针,画矩形是逆时针,没什么区别啊!?别急,后面我们学Text绘制的时候再来细说

Path的方法

moveTo(float x, float y)//将画笔起点移动到指定位置
rMoveTo(float dx, float dy)//将画笔的起点(x,y)移动到(x+dx,y+dy)
lineTo(float x, float y)//从当前终点位置画直线到(x,y)
rLineTo(float dx, float dy)//从当前终点位置(x,y)画直线到(x+dx,y+dy)
arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)//从当前终点画弧线
quadTo(float x1, float y1, float x2, float y2)//二阶贝塞尔曲线,(x1,y1)为控制点,(x2,y2)为终点
rQuadTo(float dx1, float dy1, float dx2, float dy2)//同理,代表点偏移的距离
cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)//三阶贝塞尔曲线,(x1,y1)、(x2,y2)为控制点,(x3,y3)为终点
rCubicTo(float dx1, float dy1, float dx2, float dy2,float dx3, float dy3)//同理,代表点偏移的距离

arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)

oval:弧度所在的矩形

startAngle:起始的角度

sweepAngle:起点到终点的角度

forceMoveTo:是否将画笔移动到弧的起点

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.moveTo(20, 20);
        path.lineTo(80, 20);
        path.arcTo(80, 80, 160, 140, 30, 150, false);//见下图,左边为false  右边为true
        canvas.drawPath(path, mPaint);
    }

在这里插入图片描述
在这里插入图片描述

贝塞尔曲线

贝塞尔曲线是一个很有意思的东西,深入学习的可以看下这篇博客:贝塞尔曲线

二阶贝塞尔曲线quadratic bezier

在这里插入图片描述

在二阶贝塞尔曲线中,已知三点恒定(P0,P1,P2),设定在P0 P1中的点为Pa,在P1 P2中的点为Pb,Pt在Pa Pb上的点,这三点都在相同时间t内做匀速运动,Pt滑过的路径,就是曲线的路径。

也就是说,P0和P2为曲线的起点和终点,通过P1来控制曲线的路径,说着有点像Ps中的钢笔工具啊

在这里插入图片描述

二阶贝塞尔曲线:quadTo(float x1, float y1, float x2, float y2)

我们定义P0(50,150),P2(150,150),控制点P1(100,100)

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.moveTo(50, 150);
        path.quadTo(100,100,150,150);
        canvas.drawPath(path, mPaint);
    }

在这里插入图片描述

二阶贝塞尔曲线应用:如何实现绘制手势轨迹?

首先我们用lineTo()来模拟手势轨迹效果

    private Path mPath;
    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.parseColor("#ff0000"));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(2);
        mPath = new Path();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(mPath, mPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(event.getX(),event.getY());
                invalidate();
                break;
        }
        return super.onTouchEvent(event);
    }

在这里插入图片描述

可以看到,用lineTo()的方式画出的轨迹并不圆滑,因为点与点之间是通过线段(line)连接的

那我们可以通过二阶贝塞尔曲线实现更加圆滑的手势轨迹,起点和终点好说,这个控制点该怎么确定呢?我们不防用Ps来模拟一下。

在这里插入图片描述
在这里插入图片描述

整个曲线过程为以下四步完成:

  1. path.moveTo(P0)
  2. path.quadTo(P0,(P0+P2)/2)
  3. path.quadTo(P2,(P2+P3)/2)
  4. path.quadTo(P3,(P3+P4)/2)

最后P3-P4中点到P4直接省略不画,不影响最终的结果(手势点之间的距离很密的,不可能看出来)

    private Path mPath;

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.parseColor("#ff0000"));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(2);
        mPath = new Path();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(mPath, mPaint);
    }

    private float startX,startY;
    @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_MOVE:
                mPath.quadTo(startX,startY,(startX+event.getX())/2f,(startY+event.getY())/2f);
                startX=event.getX();
                startY=event.getY();
                invalidate();
                break;
        }
        return super.onTouchEvent(event);
    }

在这里插入图片描述

三阶贝塞尔曲线cubic bezier

在这里插入图片描述

三阶贝塞尔曲线有两个控制点(P1,P2),也可以看作是二阶贝塞尔曲线,不过控制点是在P1-P2上动态变化的

三阶贝塞尔曲线:cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)

前面我们一直用Ps钢笔工具演示了曲线,其实从整个曲线来看是用的三阶贝塞尔曲线特性,因为有两个控点,只是正好两个控点是相对的,且我们观察的某一小段曲线,用二阶贝塞尔曲线是几乎不可能画出连续圆滑的一条曲线的,如下图:

在这里插入图片描述
在这里插入图片描述

而现实需求是给出几个峰值和谷值,画出走势图

在这里插入图片描述

如绿色曲线段,影响它的控点为P1和P2,x坐标为始终点的中点

在这里插入图片描述

我们用cubicTo()来模拟实现一条曲线

    @Override
    protected void onDraw(Canvas canvas) {
        ArrayList<Point> list = new ArrayList<>();
        list.add(new Point(0, 0));
        list.add(new Point(30, 60));
        list.add(new Point(60, 30));
        list.add(new Point(120, 100));
        list.add(new Point(180, 80));
        list.add(new Point(260, 160));
        list.add(new Point(400, 60));

        Path path = new Path();
        Point P1, P2;
        for (int i = 0; i < list.size(); i++) {
            if (i == 0) {
                path.moveTo(list.get(i).x, list.get(i).y);
            } else{
                P1 = new Point((list.get(i - 1).x + list.get(i).x) / 2, list.get(i-1).y);
                P2 = new Point((list.get(i - 1).x + list.get(i).x) / 2, list.get(i).y);
                path.cubicTo(P1.x,P1.y,P2.x,P2.y,list.get(i).x,list.get(i).y);
            }
        }
        canvas.drawPath(path, mPaint);
    }

在这里插入图片描述

这个例子中呢,我们的控杆是平行X轴的,所以保证了给出的点一定为波峰和波谷。

在工作中,我们常常为了使两段曲线完美衔接,采用另外一种确定控制点的方式

在这里插入图片描述

在这里插入图片描述

通过这种方式可以很好的、圆滑过度的连接两条曲线。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一鱼浅游

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值