前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >自定义控件详解(八):贝塞尔曲线

自定义控件详解(八):贝塞尔曲线

作者头像
听着music睡
发布于 2018-06-08 09:45:53
发布于 2018-06-08 09:45:53
53500
代码可运行
举报
文章被收录于专栏:Android干货Android干货
运行总次数:0
代码可运行

Path类有4个贝塞尔曲线相关方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//二阶贝赛尔  
public void quadTo(float x1, float y1, float x2, float y2)  
public void rQuadTo(float dx1, float dy1, float dx2, float dy2)  
//三阶贝赛尔  
public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)  
public void rCubicTo(float x1, float y1, float x2, float y2,float x3, float y3)  

关于贝塞尔曲线的概念就不讲了,直接看代码使用。

 一、 下面的方法中 ,参数中(x1,y1)是控制点坐标,(x2,y2)是终点坐标 

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void quadTo(float x1, float y1, float x2, float y2)  

 大家看到和Path.lineTo()方法有些不一样,它没有起始坐标。

 实际上连续使用quadTo()方法的时候,上一次使用的终点坐标即下一次的起始坐标

 而一开始我们需要用moveTo()来指定一个起始坐标,如果不指定的话,起始坐标默认为左上角(0,0)

 下面通过实现绘图板功能来看一下使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class BSELineView extends View{
    Path path = new Path();
    float mX;
    float mY;
    public BSELineView(Context context) {
        super(context);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN: {
                path.moveTo(event.getX(), event.getY());   // 触摸按下的时候,记录起始坐标
                mX = event.getX();
                mY = event.getY();
                return true;
            }
            case MotionEvent.ACTION_MOVE: {                // 触摸过程中,用贝塞尔曲线方法 quadTo()记录路径
                path.quadTo(mX, mY, (mX + event.getX()) / 2, (mY + event.getY()) / 2);
                mX = event.getX();
                mY = event.getY();
                invalidate();                              // 刷新view 注意是在UI线程中
                break;
            }
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.set
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
可以看到我们绘制出来的View 线条比较华润,没有弯折的生硬感。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
二、绘制波浪线
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void rQuadTo(float dx1, float dy1, float dx2, float dy2)  
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
dx1:控制点X坐标,表示相对上一个终点X坐标的位移坐标,可为负值,正值表示相加,负值表示相减;
dy1:控制点Y坐标,相对上一个终点Y坐标的位移坐标。同样可为负值,正值表示相加,负值表示相减;
dx2:终点X坐标,同样是一个相对坐标,相对上一个终点X坐标的位移值,可为负值,正值表示相加,负值表示相减;
dy2:终点Y坐标,同样是一个相对,相对上一个终点Y坐标的位移值。可为负值,正值表示相加,负值表示相减;
这四个参数都是传递的都是相对值,都是相对上一个终点的位移值。 

 举例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
path.moveTo(100,100);  
path.quadTo(300,50,500,500); 

相等于

path.moveTo(100,100);  
path.rQuadTo(200,-50,400,400) 

 实现一个波浪线。

 初始点假设(orginX , orginY) , 控制点(x,rangY)  ,终点(x*2 , 0)    ,  就绘制了一条开口向下的曲线

                                              接着控制点(x,-rangY) , 终点(x*2,0)   ,就接着绘制了一条开口向上的曲线

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        int originY = 300;  // 初始点y轴坐标
        int rangeY = 50 ;    // y轴幅度
        int rangeX = 50; //x轴幅度
        path.moveTo(0,originY);
        for (int i = 0; i < getWidth(); i++) {  // x 轴范围小与屏幕宽度
            path.rQuadTo(rangeX,rangeY,rangeX*2,0);
            path.rQuadTo(rangeX,-rangeY,rangeX*2,0);
            i+=(rangeX*2);
        }

完整代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class WaveView extends View{
    Paint paint ;
    Path path;

    public WaveView(Context context) {
        super(context);
    }

    public WaveView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        path = new Path();
        paint.setColor(Color.GREEN);
        paint.setStrokeWidth(6);
        paint.setStyle(Paint.Style.STROKE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        path.reset();
        int originY = 300;  // 初始点y轴坐标
        int rangeY = 50 ;    // y轴幅度
        int rangeX = 50; //x轴幅度
        path.moveTo(0,originY);
        for (int i = 0; i < getWidth(); i++) {  // x 轴范围小与屏幕宽度
            path.rQuadTo(rangeX,rangeY,rangeX*2,0);
            path.rQuadTo(rangeX,-rangeY,rangeX*2,0);
            i+=(rangeX*2);
        }

        canvas.drawPath(path,paint);
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
效果图:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
绘制移动的波形图:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class WaveView extends View{
    Paint paint ;
    Path path;
    int change;
    public WaveView(Context context) {
        super(context);
    }

    public WaveView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        path = new Path();
        paint.setColor(Color.GREEN);
        paint.setStrokeWidth(6);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        path.reset();
        int originY = 300;  // 初始点y轴坐标
        int rangeY = 50 ;    // y轴幅度
        int rangeX = 50; //x轴幅度
        path.moveTo(-rangeX*4+change,originY); // 让初始点在界面左侧4*rangX处, change是移动距离,没2000毫秒重新绘制
        for (int i = 0; i < getWidth(); i+=(rangeX*2)) {  // x 轴范围小与屏幕宽度
            path.rQuadTo(rangeX,rangeY,rangeX*2,0);
            path.rQuadTo(rangeX,-rangeY,rangeX*2,0);

        }
        path.lineTo(getWidth(),getHeight());
        path.lineTo(0,getHeight());
        path.close();

        canvas.drawPath(path,paint);
    }
    public void startAnim(){
        ValueAnimator animator = ValueAnimator.ofInt(0 , 200);
        animator.setDuration(2000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                change = (int) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.start();
    }
}
代码语言:javascript
代码运行次数:0
运行
复制
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-11-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
谈谈 Java 中自定义注解及使用场景
Java自定义注解一般使用场景为:自定义注解+拦截器或者AOP,使用自定义注解来自己设计框架,使得代码看起来非常优雅。本文将先从自定义注解的基础概念说起,然后开始实战,写小段代码实现自定义注解+拦截器,自定义注解+AOP。
好好学java
2020/09/25
9030
谈谈 Java 中自定义注解及使用场景
Java 自定义注解在登录验证的应用
从 JDK 5开始,Java 增加了注解的新功能,注解其实是代码里面的特殊标记,这些标记可以在编译、类加载和运行时被读取,在不改变代码原有逻辑下,给源文件嵌入注解信息。再通过返回获取注解信息,根据不同的注解信息处理不同逻辑。其中 Java 有以下几个元Annotation:
用户10384376
2023/02/25
5450
Java 自定义注解在登录验证的应用
Java自定义注解Annotation详解[通俗易懂]
开发中经常使用到注解,在项目中也偶尔会见到过自定义注解,今天就来探讨一下这个注解是什么鬼,以及注解的应用场景和如何自定义注解。
全栈程序员站长
2022/07/28
1.3K0
Java注解Annotation与自定义注解详解
开发中经常使用到注解,在项目中也偶尔会见到过自定义注解,今天就来探讨一下这个注解是什么鬼,以及注解的应用场景和如何自定义注解。
yaphetsfang
2020/07/30
9020
Spring 自定义注解你了解过吗?
https://juejin.im/post/5cf376e16fb9a07eee5eb6eb
南风
2020/09/11
5740
面试官:实际工作中哪里用到了自定义注解?
自定义注解可以标记在方法上或类上,用于在编译期或运行期进行特定的业务功能处理。在 Java 中,自定义注解使用 @interface 关键字来定义,它可以实现如:日志记录、性能监控、权限校验等功能。
磊哥
2024/01/13
1790
面试官:实际工作中哪里用到了自定义注解?
自定义注解限流
注解用来给类声明附加额外信息,可以标注在类、字段、方法等上面,编译器、JVM以及开发人员等都可以通过反射拿到注解信息,进而做一些相关处理
高大北
2022/06/14
2370
使用自定义注解实现接口参数校验
在接口的开发中,我们有时会想让某个接口只可以被特定的人(来源)请求,那么就需要在服务端对请求参数做校验.
呼延十
2019/06/26
1.8K0
使用自定义注解实现接口参数校验
Java自定义注解及应用
注解是在jdk1.5之后引入的,极大的方便了我们编写程序,并且上文Spring中XML,注解,JavaConfig如何选择,就说了用注解和JavaConfig比较优雅,必须得用注解和JavaConfig写个小例子啊
Java识堂
2019/08/13
1.4K0
你的开发利器Spring自定义注解
  自定义注解在开发中是一把利器,经常会被使用到。在上一篇文章中有提到了自定义校验注解的用法。然而最近接到这样一个需求,主要是针对某些接口的返回数据需要进行一个加密操作。于是很自然的就想到了自定义注解+AOP去实现这样一个功能。但是对于自定义注解,只是停留在表面的使用,没有做到知其然,而知其所以然。所以这篇文章就是来了解自定义注解这把开发利器的。
不一样的科技宅
2020/11/20
8470
你的开发利器Spring自定义注解
java注解拦截_轻松实现java拦截器+自定义注解
本文将用简洁的代码构建一个springboot的拦截器。拦截器的使用很简单,定义一个自己的拦截器,向配置中添加一下就可以使用。为了方便,之后又引入了注解。
全栈程序员站长
2022/09/02
1.6K0
java注解拦截_轻松实现java拦截器+自定义注解
java自定义注解实现「建议收藏」
去杭州第一次面试的时候问及到自定义注解,那时候不清楚,现在简单写下,算是对过去的一个交代。
全栈程序员站长
2022/07/28
3400
SpringMVC自定义注解验证登陆拦截
这里业务场景需要,所有的请求都需要登录验证。个别通用业务不需要登录拦截。注解方式替代原有的if判断。
有一只柴犬
2024/01/25
1700
java自定义注解的使用和基本原理「建议收藏」
在web开发中,权限控制非常重要,所以有些接口会限制必须登录之后才能访问,但是个别接口并没有这种限制。一种方式是把需要过滤的接口或者方法配置在文件中,每次请求时在拦截器中根据请求的路径与配置文件中的对比过滤。其实还有另外一种方式就是通过注解方式。
全栈程序员站长
2022/07/30
5090
java自定义注解的使用和基本原理「建议收藏」
java进阶|基于springAop和自定义注解进行方法的耗时统计
本想着基于aop做些比较实际一点的业务场景,但是服务器不给力,暂时没法连接上了,额,这也是自己没有去写其它内容的客观原因之一了,因为涉及不了数据库层面,所以慢慢拖到了现在,也只有在写一些代码层面的内容了,写完这篇就暂时不写了,今年还有其它事情要做。
码农王同学
2020/04/10
1.7K0
java进阶|基于springAop和自定义注解进行方法的耗时统计
Java中注解是什么?提供了一个自定义注解的实例
Java中的注解是一种元数据,它可以提供有关代码的额外信息。在Java代码中,我们可以使用注解来标记类、方法或字段,以便我们可以在运行时或编译时访问这些信息。例如,我们可以使用注解来记录代码的作者、版本号或其他元数据,或者使用注解来控制代码的行为或属性。
用户1289394
2024/06/25
1430
Java中注解是什么?提供了一个自定义注解的实例
自定义注解(拦截器实现)
拦截器是在面向切面编程中应用的,就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法。是基于JAVA的反射机制
Java king
2023/02/21
6310
【Java 基础 - 注解机制详细解释】
注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它主要的作用有以下四方面:
奥耶可乐冰
2024/05/31
1300
spring的aop注解(java自定义注解)
该篇以记录接口调用的传入参数日志为场景,来介绍下使用自定义注解作为切点,AOP切面方式去记录每个接口的传入参数以及可扩展的业务处理。
全栈程序员站长
2022/07/29
7960
spring的aop注解(java自定义注解)
Spring AOP技术原理及利用自定义注解验证数据正确性
1. 切面(Aspect):一个切面由多个通知(Advice)组成,代表了交叉业务逻辑的模块。通知包含了要在目标方法执行前后执行的代码。
用户7353950
2024/04/30
3130
Spring AOP技术原理及利用自定义注解验证数据正确性
相关推荐
谈谈 Java 中自定义注解及使用场景
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档