Android_贝塞尔曲线

来源:互联网 发布:网络语言粗鄙化的原因 编辑:程序博客网 时间:2024/06/07 02:59

一、贝塞尔函数

使用贝塞尔函数可以画平滑的曲线,经常使用的是二阶的贝塞尔函数

//二阶贝赛尔  //参数中(x1,y1)是控制点坐标,(x2,y2)是终点坐标public void quadTo(float x1, float y1, float x2, float y2)   
//dx1:控制点X坐标,表示相对上一个终点X坐标的位移坐标,可为负值,正值表示相加,负值表示相减;//dy1:控制点Y坐标,相对上一个终点Y坐标的位移坐标。同样可为负值,正值表示相加,负值表示相减;//dx2:终点X坐标,同样是一个相对坐标,相对上一个终点X坐标的位移值,可为负值,正值表示相加,负值表示相减;//dy2:终点Y坐标,同样是一个相对,相对上一个终点Y坐标的位移值。可为负值,正值表示相加,负值表示相减;public void rQuadTo(float dx1, float dy1, float dx2, float dy2) 

使用二阶的贝塞尔函数 quadTo 方法画如下的曲线
这里写图片描述
这是再简单不过的代码,参照注释可以理解

public class MyBezier extends View {Context mContext;Path mPath;Paint mPaint;public MyBezier(Context context) {this(context,null);}public MyBezier(Context context, AttributeSet attrs) {    super(context, attrs);    mContext=context;    init(mContext);}public void init(Context context){    mPath=new Path();    mPaint=new Paint();    mPaint.setColor(0xffff0000);    //设置抗锯齿    mPaint.setAntiAlias(true);    mPaint.setStyle(Paint.Style.STROKE);}//这个是核心的方法@Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    //这个方法中填起点坐标,也就是图中的P0    mPath.moveTo(100,300);    //参数1和参数2:表示控制点,也就是图中的P1    //参数3和参数4:表示终点,也就是图中的P2    mPath.quadTo(200,100,300,300);    //参数的含义和上面的一样    mPath.quadTo(400,500,500,300);    canvas.drawPath(mPath,mPaint);    }}

整条线的起始点是通过Path.moveTo(x,y)来指定的,而如果我们连续调用quadTo(),前一个quadTo()的终点,就是下一个quadTo()函数的起点,参照上面的代码可以看出,两个quadTo()之间并没有moveTo()方法,来指定起点,这是因为quadTo()连续调用,系统默认为下一个quadTo()方法的起点是上一个quadTo()的终点。
如果初始没有调用Path.moveTo(x,y)来指定起始点,则默认以控件左上角(0,0)为起始点

二、用手指划没有棱角的曲线

一,有棱角的曲线

如果不使用贝塞尔曲线去画一条曲线,你会发现曲线有棱角,代码和效果如如下
,仔细观察效果图,可以发现,曲线部分有很明显的棱角,使用贝塞尔曲线,就可以消除这种棱角
这里写图片描述

两个地方需要注意:
第一:有关在case MotionEvent.ACTION_DOWN时return true的问题:return true表示当前控件已经消费了下按动作,之后的ACTION_MOVE、ACTION_UP动作也会继续传递到当前控件中;如果我们在case MotionEvent.ACTION_DOWN时return false,那么后序的ACTION_MOVE、ACTION_UP动作将不会再传递该控件

第二:这里重绘控件使用的是invalidate();当然也可以使用postInvalidate()函数的。这两个函数的作用都是用来重绘控件的,但区别是invalidate()一定要在UI线程执行,如果不是在UI线程就会报错。而postInvalidate()则没有那么多讲究,它可以在任何线程中执行,而不必一定要是主线程。其实在postInvalidate()就是利用handler给主线程发送刷新界面的消息来实现的,所以它是可以在任何线程中执行,而不会出错。而正是因为它是通过发消息来实现的,所以它的界面刷新可能没有直接调Invalidate()刷的那么快
所以在我们确定当前线程是主线程的情况下,还是以invalide()函数为主。当我们不确定当前要刷新页面的位置所处的线程是不是主线程的时候,还是用postInvalidate为好

二,没有棱角的曲线(使用qaudTo方法)
代码如下,代码不是很多,主要理解其中的核心思想.代码中的核心是onTouchEvent方法中逻辑,我们来分析一下

画线分为两个部分,一个是画直线,一个是画曲线,所以代码中的逻辑要符合这两个部分,不能只画曲线,而代码的逻辑不符合画直线的思想.
1.画直线的分析(主要分析onTouchEvent的中的代码)
假设我们的起始点在(0,0),向终点(10,10)画直线(直线上的其它点省略)

mPreX=0,mPreY=0; endX=5,endY=5;

quadTo的参数为quadTo(0,0,5,5); 也就是说控制点是(0,0),终点是(5,5),起点也是(0,0),所以简单的分析,就可以得出,图形为直线

2.画曲线的分析(主要分析onTouchEvent的中的代码)
假设我们的起始点在(0,0),在(10,10)处拐了个弯,终点是(20,10)(直线上的其它点省略)
大致图形如下
这里写图片描述
(1)moveTo方法中的参数:moveTo(0,0),但是mPreX和mPreY并不是(0,0),因为在执行MotionEvent.ACTION_MOVE方法的时候已经将mPreX和mPreY的点改为了(10,10),也就是图中的B.当到达C点时,画曲线就结束了,这时候个方法的参数如下

mPath.moveTo(0,0)mPath.quadTo(10,10,20,10);

三、动态的水波
下面的代码实现了水波的效果,主要是属性动画和贝塞尔函数的结合
下面这几篇是关于属性动画(Animator)的使用
http://blog.csdn.net/iispring/article/details/50322625
http://blog.csdn.net/guolin_blog/article/details/43536355
http://blog.csdn.net/guolin_blog/article/details/43536355

public class MyView5 extends View {    Path mPath;    Context mContext;    Paint mPaint;    // 单个波的长度    int mWaveLength = 900;    // 波的高度    int mWaveHeight = 700;    // 水平漂移    int distanceX = 0;    // 动画    ValueAnimator mValueAnimator;public MyView5(Context context) {    this(context, null);}public MyView5(Context context, AttributeSet attrs) {    super(context, attrs);    init(context);}//这句话的意思是只有在level大于21的版本才能使用@TargetApi(Build.VERSION_CODES.LOLLIPOP)private void init(Context context) {mPath = new Path();mPaint = new Paint();mPaint.setColor(Color.BLUE);mPaint.setAntiAlias(true);mPaint.setStyle(Paint.Style.FILL);mContext = context;//设置动画为颜色的变化//从0xffff0000渐变到0xff00ff00,再到0xff0000ff//周期为6s,匀速变化mValueAnimator = ValueAnimator.ofArgb(0xffff0000, 0xff00ff00, 0xff0000ff);mValueAnimator.setDuration(6000);mValueAnimator.setInterpolator(new LinearInterpolator());//动画的监听,在这个例子中也就是颜色改变,就实现下面的回调mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {    @Override    public void onAnimationUpdate(ValueAnimator animation) {        float fraction = animation.getAnimatedFraction();        // 计算漂移量        distanceX = (int) (mWaveLength * fraction * 10);        // 画笔颜色        mPaint.setColor((Integer) animation.getAnimatedValue());        // 重绘        invalidate();    }});startAnim();}@Overrideprotected void onDraw(Canvas canvas) {// 1/4水波宽度int halfWaveLength = mWaveLength / 2;//将mPath清空mPath.reset();//在这里需要注意,比较容易懵逼//distanceX % mWaveLength的值不会大于一个波长//所以-mWaveLength + distanceX % mWaveLength的值不会大于零mPath.moveTo(-mWaveLength + distanceX % mWaveLength, mWaveHeight++);// 绘制n个波,每个波由两个二阶曲线组成for (int i = -mWaveLength; i <= getWidth() + mWaveLength; i += mWaveLength) {mPath.rQuadTo(halfWaveLength / 2, -100, halfWaveLength, 0);mPath.rQuadTo(halfWaveLength / 2, 100, halfWaveLength, 0);}// 封闭水波之下的区域mPath.lineTo(getWidth(), getHeight());mPath.lineTo(0, getHeight());mPath.close();canvas.drawPath(mPath, mPaint);}    //暂时用不到public void reset() {    mPath.reset();    distanceX = 0;    mWaveHeight = 700;    invalidate();    }    //动画的开启public void startAnim() {    mValueAnimator.start();    }}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 ie文件不存在了怎么办 大学素质分不够怎么办 素拓学分不够怎么办 大学毕业之前素拓分拿不满怎么办 武汉幼儿医保卡怎么办 养老院护工欺老人怎么办 皮肤毛孔粗大有痘印痘坑怎么办 额头上毛孔大怎么办 额头皮肤毛孔大怎么办 脸部粗糙毛孔大怎么办 脸上有痘印毛孔粗大怎么办 毛孔粗大痘印怎么办 教官12123一直加载怎么办 教官嗓子哑了怎么办 喜欢上考场教官怎么办 跟教官打起来怎么办 车险贴丢了怎么办 大学不想军训该怎么办 职高军训不想去怎么办 上大学不想军训怎么办 收费站忘记带钱怎么办 孕妇咳得厉害怎么办 怀孕九个月咳嗽怎么办 门冬氨酸高怎么办 代理保证金不退怎么办 电脑游戏太大下载慢怎么办 四川百裕制药怎么办 想退出学校中层怎么办 眼镜度数高了怎么办 眼睛散光200度怎么办 火车漏乘旅客怎么办 钓鱼逆风走漂怎么办 藏红花喝了上火怎么办 领导经常关注你怎么办 点读机屏幕坏了怎么办 步步高点读机坏了怎么办 健身房体测缺水怎么办 标书有效期过了怎么办 电脑被限制连接怎么办 win7无线网络受限制怎么办 宝宝没有体检本怎么办